Родитель
021b97c88b
Коммит
297e86c010
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче