Instantly share code, notes, and snippets.
Created
November 7, 2017 11:53
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save cravindra/fc57dd7e322c5e179f4922f7bc0a04e7 to your computer and use it in GitHub Desktop.
Middleware arrangement for using Express Session with session ID which can be overriden by a URL query parameter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const express = require('express'); | |
const router = express.Router(); | |
const session = require('express-session'); | |
const MemcachedStore = require('connect-memcached')(session); | |
const _ = require('lodash'); | |
const utils = require('../../services/application/utils'); | |
const config = utils.getConfiguration(); | |
/** | |
* Middleware which goes in before Express Session to enable overriding session ID assignment through a query parameter | |
* or a request header `uid` | |
* @param req - The Express Request Object | |
* @param res - The Express Response Object | |
* @param next | |
*/ | |
function overrideSessionAssignment(req, res, next) { | |
/* Hack to fool express session middleware to think that we are using the legacy cookie-parse middleware | |
* to enable session assignment through headers/query parameters for cookieless flows | |
* | |
* To retrieve the session ID, the Express Session middleware first checks req.headers.cookie and tries to | |
* parse the session ID from there. If it doesn't find this value, it checks for it in req.signedCookies (that's | |
* where cookie-parser usually puts signed cookies) for backward compatibility with the older method of using | |
* cookie-parse middleware in conjunction with the express session middleware. | |
* | |
* This middleware simulates this by deleting the req.headers.cookie field and setting the value obtained | |
* from the URL or headers into req.signedCookies[<cookie_name>]] | |
* | |
* [Interesting conversation on the Security Implications](https://github.com/expressjs/session/pull/159) | |
* | |
* References: | |
* 1. Express Session Source Code where this check happens | |
* [Express Session: index.js#L547](https://github.com/expressjs/session/blob/89fd7156129210f2b0c350afcbdf226665a8328c/index.js#L537) | |
* | |
* 2. Issues and PRs Regarding This topic on Express Session: | |
* [Adds optional session getter Pull#40](https://github.com/expressjs/session/pull/40) | |
* [Manually set sessionID Pull#159](https://github.com/expressjs/session/pull/159) | |
* [Can't set manual ID Issue#158](https://github.com/expressjs/session/issues/158) | |
* [Cookies disabled results in loss of session (no workaround via Header) Issue#185](https://github.com/expressjs/session/issues/185) | |
* [Trouble upgrading to Express 4 + standalone session middleware... Issue#148](https://github.com/expressjs/session/issues/148) | |
*/ | |
if (req.query.uid || req.headers.sl_uid) { | |
//Save the cookie header if any and delete the cookie header | |
req.headers._sl_cookie = '' + req.headers.cookie; | |
delete req.headers.cookie; | |
//Add the expected key under signedCookies | |
//This will trigger a deprecated warning in the logs from express session - we can ignore that. | |
req.signedCookies = req.signedCookies || {}; | |
req.signedCookies[config.application.session.middleware.name] = req.query.uid || req.headers.uid; | |
} | |
next(); | |
} | |
/** | |
* Middleware to run after express session to let other middleware down the chain to treat this as a cookieless session | |
* if the session was over written by the query or the headers. | |
* @param req - The Express Request Object | |
* @param res - The Express Response Object | |
* @param next | |
*/ | |
function initializeNoCookieSession(req, res, next) { | |
if (req.query.uid || req.headers.uid) { | |
req.session.cookieless = true; | |
req.session.nocookie = req.session.nocookie || {}; | |
} | |
next(); | |
} | |
//Override Session assignment if needed | |
router.use(overrideSessionAssignment); | |
//Express Session Setup | |
let sessionConfig = _.merge({}, config.application.session.middleware); | |
//Use Memcached if production or stage. else, use default memory store | |
sessionConfig.store = (config.instance.env !== 'dev') ? new MemcachedStore(sessionConfig.store) : undefined; | |
router.use(session(sessionConfig)); | |
//Override as no cookies if session ID was overwritten using query or header | |
router.use(initializeNoCookieSession); | |
module.exports = router; | |
/** | |
* TODO: Investigate if there is any way to optimize TTL of no cookie session. | |
* Perhaps make session treat it like a session cookie? | |
* Sessions with no cookies will not be relevant after the window is closed but will still be occupying space in storage | |
* Fixing these sessions with a short ttl or some such other strategy will see large performance gains at scale | |
*/ | |
/** | |
* TODO: Fix session hijacking vulnerability. | |
* Hack scenario: | |
* 1. Attacker opens appplication and retrieves the session ID (either from in memory or from the URL | |
* 2. Attacker shares URL with UID parameter as query to the Target | |
* 3. Target opens URL using the uid which overrides the cookie flow | |
* 4. Target logs in | |
* 5. Attacker can now hijack the session as the session id remains the same. | |
* | |
* Possible Solutions: | |
* 1. Always give the cookies, if present, priority to determine the session [simple] | |
* 2. Refresh the session ID after authentication [R&D needed. How would one do that using Express Session] | |
* | |
* | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment