Skip to content

Commit 051fe3e

Browse files
Merge pull request #835 from solid/fix/allow-subdomain-cookies
Block cookie auth from non-subdomains, but allow all other auth from everywhere
2 parents 8a28461 + 0a48614 commit 051fe3e

File tree

3 files changed

+50
-32
lines changed

3 files changed

+50
-32
lines changed

lib/create-app.js

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,27 @@ function initWebId (argv, app, ldp) {
169169
const useSecureCookies = !!argv.sslKey // use secure cookies when over HTTPS
170170
const sessionHandler = session(sessionSettings(useSecureCookies, argv.host))
171171
app.use(sessionHandler)
172+
// Reject cookies from third-party applications.
173+
// Otherwise, when a user is logged in to their Solid server,
174+
// any third-party application could perform authenticated requests
175+
// without permission by including the credentials set by the Solid server.
176+
app.use((req, res, next) => {
177+
const origin = req.headers.origin
178+
const userId = req.session.userId
179+
// Exception: allow logout requests from all third-party apps
180+
// such that OIDC client can log out via cookie auth
181+
// TODO: remove this exception when OIDC clients
182+
// use Bearer token to authenticate instead of cookie
183+
// (https://github.com/solid/node-solid-server/pull/835#issuecomment-426429003)
184+
if (!argv.host.allowsSessionFor(userId, origin) && !isLogoutRequest(req)) {
185+
debug(`Rejecting session for ${userId} from ${origin}`)
186+
// Destroy session data
187+
delete req.session.userId
188+
// Ensure this modified session is not saved
189+
req.session.save = (done) => done()
190+
}
191+
next()
192+
})
172193

173194
let accountManager = AccountManager.from({
174195
authMethod: argv.auth,
@@ -187,30 +208,20 @@ function initWebId (argv, app, ldp) {
187208
// Set up authentication-related API endpoints and app.locals
188209
initAuthentication(app, argv)
189210

190-
// Protect against requests from third-party applications
191-
app.use((req, res, next) => {
192-
// Reject cookies from third-party applications.
193-
// Otherwise, when a user is logged in to their Solid server,
194-
// any third-party application could perform authenticated requests
195-
// without permission by including the credentials set by the Solid server.
196-
const origin = req.headers.origin
197-
const userId = req.session.userId
198-
if (!argv.host.allowsSessionFor(userId, origin)) {
199-
debug(`Rejecting session for ${userId} from ${origin}`)
200-
// Destroy session data
201-
delete req.session.userId
202-
// Ensure this modified session is not saved
203-
req.session.save = done => done()
204-
}
205-
next()
206-
})
207-
208-
// Set up per-host LDP middleware
209211
if (argv.multiuser) {
210212
app.use(vhost('*', LdpMiddleware(corsSettings)))
211213
}
212214
}
213215

216+
/**
217+
* Determines whether the given request is a logout request
218+
*/
219+
function isLogoutRequest (req) {
220+
// TODO: this is a hack that hard-codes OIDC paths,
221+
// this code should live in the OIDC module
222+
return req.path === '/logout' || req.path === '/goodbye'
223+
}
224+
214225
/**
215226
* Sets up authentication-related routes and handlers for the app.
216227
*

lib/models/solid-host.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,14 @@ class SolidHost {
7272
allowsSessionFor (userId, origin) {
7373
// Allow no user or an empty origin
7474
if (!userId || !origin) return true
75-
// Allow the server's main domain
76-
if (origin === this.serverUri) return true
77-
// Allow the user's subdomain
78-
const userIdHost = userId.replace(/([^:/])\/.*/, '$1')
79-
if (origin === userIdHost) return true
80-
// Disallow everything else
75+
// Allow the server and subdomains
76+
const originHost = getHostName(origin)
77+
const serverHost = getHostName(this.serverUri)
78+
if (originHost === serverHost) return true
79+
if (originHost.endsWith('.' + serverHost)) return true
80+
// Allow the user's own domain
81+
const userHost = getHostName(userId)
82+
if (originHost === userHost) return true
8183
return false
8284
}
8385

@@ -109,4 +111,9 @@ class SolidHost {
109111
}
110112
}
111113

114+
function getHostName (url) {
115+
const match = url.match(/^\w+:\/*([^/]+)/)
116+
return match ? match[1] : ''
117+
}
118+
112119
module.exports = SolidHost

test/unit/solid-host-test.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,23 @@ describe('SolidHost', () => {
5555
})
5656

5757
it('should allow a userId with empty origin', () => {
58-
expect(host.allowsSessionFor('https://user.test.local/profile/card#me', '')).to.be.true
58+
expect(host.allowsSessionFor('https://user.own/profile/card#me', '')).to.be.true
5959
})
6060

6161
it('should allow a userId with the user subdomain as origin', () => {
62-
expect(host.allowsSessionFor('https://user.test.local/profile/card#me', 'https://user.test.local')).to.be.true
62+
expect(host.allowsSessionFor('https://user.own/profile/card#me', 'https://user.own')).to.be.true
6363
})
6464

65-
it('should disallow a userId with another subdomain as origin', () => {
66-
expect(host.allowsSessionFor('https://user.test.local/profile/card#me', 'https://other.test.local')).to.be.false
65+
it('should allow a userId with the server domain as origin', () => {
66+
expect(host.allowsSessionFor('https://user.own/profile/card#me', 'https://test.local')).to.be.true
6767
})
6868

69-
it('should allow a userId with the server domain as origin', () => {
70-
expect(host.allowsSessionFor('https://user.test.local/profile/card#me', 'https://test.local')).to.be.true
69+
it('should allow a userId with a server subdomain as origin', () => {
70+
expect(host.allowsSessionFor('https://user.own/profile/card#me', 'https://other.test.local')).to.be.true
7171
})
7272

7373
it('should disallow a userId from a different domain', () => {
74-
expect(host.allowsSessionFor('https://user.test.local/profile/card#me', 'https://other.remote')).to.be.false
74+
expect(host.allowsSessionFor('https://user.own/profile/card#me', 'https://other.remote')).to.be.false
7575
})
7676
})
7777

0 commit comments

Comments
 (0)