encode cookieName into cookie content

fixes #35
This commit is contained in:
Sean McArthur 2013-04-19 15:22:04 -07:00
Родитель 021b97c88b
Коммит 297e86c010
2 изменённых файлов: 76 добавлений и 6 удалений

Просмотреть файл

@ -4,6 +4,8 @@ var Proxy = require("node-proxy");
var Handler = require("./ProxyHandler.js");
var crypto = require("crypto");
const COOKIE_NAME_SEP = '=';
function base64urlencode(arg) {
var s = new Buffer(arg).toString('base64');
s = s.split('=')[0]; // Remove any trailing '='s
@ -38,6 +40,10 @@ function encode(opts, content, duration, createdAt){
// format will be:
// iv.ciphertext.createdAt.duration.hmac
if (!opts.cookieName) {
throw new Error('cookieName option required');
}
if (!opts.encryptionKey) {
opts['encryptionKey'] = deriveKey(opts.secret, 'cookiesession-encryption');
}
@ -53,8 +59,9 @@ function encode(opts, content, duration, createdAt){
var iv = crypto.randomBytes(16);
// encrypt with encryption key
var plaintext = opts.cookieName + COOKIE_NAME_SEP + JSON.stringify(content);
var cipher = crypto.createCipheriv('aes256', opts.encryptionKey, iv);
var ciphertext = cipher.update(JSON.stringify(content), 'utf8', 'binary');
var ciphertext = cipher.update(plaintext, 'utf8', 'binary');
ciphertext += cipher.final('binary');
ciphertext = new Buffer(ciphertext, 'binary');
@ -80,6 +87,10 @@ function decode(opts, content) {
if (components.length != 5)
return;
if (!opts.cookieName) {
throw new Error("cookieName option required");
}
if (!opts.encryptionKey) {
opts['encryptionKey'] = deriveKey(opts.secret, 'cookiesession-encryption');
}
@ -118,9 +129,14 @@ function decode(opts, content) {
var plaintext = cipher.update(ciphertext, 'utf8');
plaintext += cipher.final('utf8');
var cookieName = plaintext.substring(0, plaintext.indexOf(COOKIE_NAME_SEP));
if (cookieName !== opts.cookieName) {
return;
}
try {
return {
content: JSON.parse(plaintext),
content: JSON.parse(plaintext.substring(plaintext.indexOf(COOKIE_NAME_SEP) + 1)),
createdAt: createdAt,
duration: duration
};
@ -197,6 +213,8 @@ Session.prototype = {
this.clearContent();
var unboxed = decode(this.opts, content);
if (!unboxed) return;
var self = this;
Object.keys(unboxed.content).forEach(function(k) {
@ -354,4 +372,4 @@ module.exports = cookieSession;
module.exports.util = {
encode: encode,
decode: decode
};
};

Просмотреть файл

@ -1,7 +1,7 @@
// a NODE_ENV of test will supress console output to stderr which
// connect likes to do when next() is called with a non-falsey error
// message. We test such codepaths here.
process.env['NODE_ENV'] = 'test';
process.env.NODE_ENV = 'test';
var vows = require("vows"),
assert = require("assert"),
@ -12,6 +12,7 @@ var vows = require("vows"),
function create_app() {
// set up the session middleware
// XXX: same secret is important for a test
var middleware = cookieSessions({
cookieName: 'session',
secret: 'yo',
@ -207,7 +208,7 @@ suite.addBatch({
});
app.get("/bar", function(req, res) {
delete req.session['bar'];
delete req.session.bar;
res.send("bar");
});
@ -765,7 +766,7 @@ suite.addBatch({
});
var browser = tobi.createBrowser(app);
browser.get("/foo", function(res, $) {});
browser.get("/foo", function(res, $){});
},
"We can write to both stores": function(err, req) {
req.session.foo = 'bar';
@ -779,4 +780,55 @@ suite.addBatch({
}
});
suite.addBatch({
"swapping two cookies": {
topic: function() {
var self = this;
var app = create_app(); //important that they use the same secret
app.get('/foo', function(req, res) {
req.session.foo = 'bar';
req.securestore.foo = 'buzz';
req.securestore.widget = 4;
res.send('hello');
});
app.get('/bar', function(req, res) {
self.callback(null, req);
res.send('bye');
});
tobi.createBrowser(app).get('/foo', function(res, $){
var cookies = res.headers['set-cookie'];
var firstCookie = cookies[0];
var secondCookie = cookies[1];
function getCookieName(cookieHeader) {
return cookieHeader.substring(0, cookieHeader.indexOf('='));
}
function getCookieValue(cookieHeader) {
return cookieHeader.substring(cookieHeader.indexOf('='), cookieHeader.indexOf(';'));
}
var firstHijack = getCookieName(firstCookie) + getCookieValue(secondCookie);
var secondHijack = getCookieName(secondCookie) + getCookieValue(firstCookie);
// new browser, because tobi overwrites the passed cookies
// header with its cookie jar, so we need a new jar
tobi.createBrowser(app).get('/bar', {
headers: { 'Cookie': firstHijack + '; ' + secondHijack }
}, function(res, $){});
});
},
"doesn't keep using cookie": function(err, req) {
// session.foo should not be what securestore.foo was, or else
// we swapped cookies!
assert.notEqual(req.session.foo, 'buzz');
assert.notEqual(req.session.widget, 4);
assert.notEqual(req.securestore.foo, 'bar');
}
}
});
suite.export(module);