Skip to content

Instantly share code, notes, and snippets.

@cravindra
Created November 7, 2017 11:53
Show Gist options
  • Save cravindra/fc57dd7e322c5e179f4922f7bc0a04e7 to your computer and use it in GitHub Desktop.
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
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