Using cookieParser() and cookieSession() together? - node.js

cookieParser() gives us the option of signing cookies with a secret sentence, which is great to prevent tampering. I understand that a cookie is signed with a special value, to prevent tampering.
I just discovered cookieSession(), which I find to be a great alternative to server-stored cookies (I only store { loggedIn = true, userId=763487246824632}, it never grows).
But... I found that setting a "secret" for cookieParser() breaks things, and cookieSession() stops working if the secret sentence matches.
The reason seems to be that if the cookie is signed using the same secret, then cookieParser() actually takes it and parses it. The strange thing is that once cookieParser() has done its work, and with the same signature secret, the session is set to:
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
Rather than:
{ testing: 'OOO' }
(Each reload adds an 'o')
So...
Did my analysis get it right?
Do you know why the session is set to that strange { cookie object if the secret sentences match?
Merc.

Your analysis is correct, I can reproduce it.
The issue is caused by this line in the cookieSession middleware (some context: options.secret is the key passed to cookieSession, req.secret is the key passed to cookieParser): if you pass both middleware a secret key, cookieSession assumes that it will find the raw (unparsed) cookie in req.cookies.
But since cookieParser has picked up the signed cookie as well (and it's being run before cookieSession), it has parsed the cookie itself (and because the signing keys were the same, it succeeded to do so), stored it in req.signedCookies and deleted it from req.cookies. So as far as cookieSession is concerned, the cookie just isn't set.
The object you see is the default session contents (which is the cookie property from the cookieSession configuration):
app.use(express.cookieSession({
cookie : { // <-- this object
...
}
});
As for a solution: either use a different key for each middleware, or just pass one of them your secret key, but not both (with the understanding that if you pass it to cookieParser, all your cookies will be signed).
FWIW: I'm not entirely sure if this is a real bug. It's a consequence of using the same signing mechanism for both cookieParser and cookieSession, with no distinction between cookies signed by one or the other. Although it could be fixed by always checking if the cookie is located in req.signedCookies.

Related

What is the additional information in connect.sid cookie string apart from the session id?

I am using Node JS with express-session.
One question answered here advises to use req.cookies['connect.sid'] to get the session ID. Another answer suggests I use req.sessionID
When I compare the two the req.cookies['connect.sid'] has a string like the following:
s:G1wOJoUAhhemRQqCs7dAGlMIk5ZGaJUg.z1/HrHTfndRqKpXza8qWuwHLS067xrWfVgqTDDGYoos
req.sessionID has a string like the following:
G1wOJoUAhhemRQqCs7dAGlMIk5ZGaJUg
If the second string is the session ID (G1wOJoUAhhemRQqCs7dAGlMIk5ZGaJUg), what is the other information in the connect.sid cookie?
Tried looking for the answer via google and other websites with no luck.
Thanks,
Darren
express-session stores all session information server-side. If you use an sql database, you'd have a table for your sessions, that would look like this :
sid | sess | expire
R02GJn2GoyaqRyten1BEGbHa0XCbj529 | {"cookie": "originalMaxAge":null,"expires":null,"httpOnly":true,"path":"/"},"mybool":true,"userid":16}
That's the answer to your question, and a short explanation of what the data means, sessionID is just a (primary) key to access the data that is only available server-side.
Now from your question it looks like you're planning on using express-session wrong.
To use express-session on your express server you would include it like so :
const session = require('express-session');
app.use(session({
name : 'mycookie',
secret : 'mysecret',
saveUninitialized : false,
resave : true
}));
and every request that goes into all subsequent routes after this will have a .session attribute. In any case, you should never need to access the session id yourself, the middleware itself authenticates every request against whatever store you used for express-session.
app.get('/givemeasession',function(req,res,next){
req.session.mybool = true;
req.session.somedata = 'mystring';
req.session.evenobjects = { data : 'somedata' };
res.send('Session set!');
});
app.get('/mysession',function(req,res,next){
res.send('My string is '+req.session.somedata+' and my object is '+JSON.stringify(req.session.evenobjects));
});
Bottomline : you shouldn't ever need to access the cookie yourself, or do anything with it, because any cookie auth is automatically handled by the middleware.
The accepted answer is correct by saying, don't handle cookies and sessions by yourself, but I don't think it answers the question:
The second part in connect.sid is the cookie signature.
In case of express-session this is generated by the package cookie-signature with the secret you set when you set up the session middleware.
As you can see in the source-code, the very first step is verifying if the cookie was signed by the secret. Afterwards the cookie is retrieved from the store.
(In case of the memory store, the retrieval also deletes the cookie if expired)
Source: https://github.com/expressjs/session/blob/1010fadc2f071ddf2add94235d72224cf65159c6/index.js#L542

Node.js. Encrypted vs Signed cookies

I want to retrieve values from my cookie.
I am using passport.js, and In this quest, I have tried different things.
I run req.cookies; which gives me this:
's:x05d6V5Dhf6efFGjIkO26Ka1.imN4lT7OhW83Nc9Z7vw5dFbCoQhanP3aa37iyNWQyvU' }
Is this a signed cookie or an encrypted cookie?
The 's:' in the beginning makes it seem like a signed one, however when I ran req.signedCookies;, I got back an empty object {}. So is this cookie signed or encrypted?
I then ran: req.sessionID; which gave back this:
x05d6V5Dhf6efFGjIkO26Ka1
As you can see, this is also contained in my cookie, before the dot. So I guess the sessionId is stored in my cookie, right?
Then I ran req.secret; which in turn returned undefined.
Is the secret in the session, which is declared here:
app.use(express.session({ secret: 'blablablabla' }));
used to sign the cookie or to encrypt it?
The cookie-signature module can only unsign a cookie and not decrypt them - is that correct?
It can unsign it based on the previous session secret?
Finally, where is this cookie stored? In my mongo db?
The part after the dot:
imN4lT7OhW83Nc9Z7vw5dFbCoQhanP3aa37iyNWQyvU
is the signature of the session id:
x05d6V5Dhf6efFGjIkO26Ka1
That is to say, the session id is encrypted with the secret and append to the session id with a dot to compose the cookie.
The secret is not contained in the cookie.
The cookie-signature module can unsign a cookie if you provided the right info:
cookie-signature.ungisn(`${the_original_sessionid}.${the_encypted_sessionid}`,secret)

Node.Js: Signed cookie can't be found

Using a MEAN environment (with express 4), I create a cookie like this.
//webserver.js
app.use(cookieParser(„somesecretkey“));
//somescript.js
res.cookie(‚testcookie‘, ‚testvalue', {signed: true, maxAge: 999999, httpOnly: true});
In another script, I try to check the existence of the cookie like this.
//someotherscript.js
if(req.cookies.testcookie){
console.log("COOKIE EXISTS“+req.cookies.testcookie);
}else{
console.log(„NO COOKIE“+req.cookies.testcookie); //always undefined
}
I checked the browser for the cookie and it definitely exists but the console keeps logging that there is no cookie (cookie undefined) when I press refresh or simply visit the page. As soon as I change the cookie to unsigned and remove the secret key, I can access it!? Why can’t the cookie be found once its signed?
The expressjs documentation for res.cookie tells us:
When using cookie-parser middleware, this method also supports signed
cookies. Simply include the signed option set to true. Then
res.cookie() will use the secret passed to cookieParser(secret) to
sign the value.
res.cookie('name', 'tobi', { signed: true });
Later you may access this value through the req.signedCookie object.
So:
did you specific a secret using cookieParser?
you should check for the cookie in req.signedCookie, not req.cookies

is it reasonable to get sessionid from url other than cookie? something about express-session

I have some domain( maybe change very frequently), and two stable domain(e.g. auth.aaa.com, api.aaa.com).
Since express-session(https://www.npmjs.com/package/express-session) default get sessionid from cookie, but when crossing domain,ajax won't send cookie( I don't want to use something like Access-Control-Allow-Credentials ).
I want to add the sessionid to the querystring, and forge a cookie before express-session middleware.
app.use(function(req,res,next){
var ss = req.query.ss;
if(ss){
var signature = require('cookie-signature');
var cookie = require('cookie');
var signed = 's:' + signature.sign(ss, "secret");
var data = cookie.serialize('jsessionids', signed);
req.headers.cookie = data;
}
next();
})
app.use(session({
name:'jsessionids',
store: new redisStore({
host:config.redis.host,
port:config.redis.port,
pass : config.redis.password,
db: config.redis.database
}),
resave: false, // don't save session if unmodified
saveUninitialized: false, // don't create session until something stored
secret: 'secret'
}));
is it reasonable? or any suggestion else?
It is generally not advisable to add the session as a query parameter, you have to jump through lots of hoops to get them to near the same level of security as cookies.
The main problem is that it is much more vulnerable to session fixation or session hijacking, which is where an attacker can steal and use another user's session.
Some key points to take into consideration
Query parameters are stored in browser history, bookmarks and referrer headers (just to name a few) which
could allow an attacker to use another users session on a shared
environment. Query string based sessions are much easier to leak outside their intended scope.
Cookies have better security mechanisms built in such as the
httpOnly flag which makes the cookies in-accessible to JavaScript
(whereas query strings are always accessible). The secure flag makes
sure that cookies are only sent over a secure connection (You could
perhaps use HSTS to help guard against MITM attacks for query string).
A user who share a link with their sessionID in the query string
which would allow any other user to assume their identity.
If you do decide to use the sessionID in the query string make sure you set an expiration time for the session and always to use TLS to securely transmit the session (same applies to any authentiction method).
Saying that, If you can avoid using query string based sessions, I would advise you do.

CSRF token issues with Express

I am trying to get CSRF protection working using csurf and express. My app uses Angular for the front end, so I figured adding this to my app would be enough:
app.use(cookieParser('test secret'));
app.use(cookieSession({
secret: 'test secret'
}));
app.use(csrf({
cookie: {
key: 'XSRF-TOKEN'
}
}));
However, when I try to make POST requests, I get 403 ("invalid CSRF token") errors.
As far as I can tell, the issue resides in the csrf-tokens module: at line 44, the expected variable looks like qMnHLQGhivxECx5WtwuktDNA-snimacq30z-XNh2X-KTpdlkU6Og while the secret and token variables look like qMnHLQGhivxECx5WtwuktDNA. Since expected and token are different, a 403 error occurs.
I have, so far, tried the following:
Setting secure: false and/or signed: true in my csurf options
Making sure that my cookie-session and cookie-parser middleware run before csurf
Re-installing all my npm modules just in case
Yelling at my computer
I could, potentially, switch to session-based tokens, but that wouldn't work as smoothly with Angular, and I'm not sure if it would even solve my problem.
Does anyone know how to fix this issue, or what the cause might be?
P.S. By the way, I've also noticed a (probably unrelated) issue: the req.csrfToken() method seems to return strings whose values are entirely unrelated to the expected value in csrf-tokens or to the string being stored in the cookie.
There is an error/bad implementation regarding csurf lib. The cookie "XSRF-TOKEN" does not store the csrf Token, but the secret that csurf uses to generate the csrf token. Therefore, the Angular part of your app identifies the XSRF-TOKEN and appends the header correctly to HTTP requests, but the csurf middleware does not recognize the secret as a valid CSRF token itself. To fix this problem, you should implement this as:
var cookieParser = require('cookie-parser');
var cookieSession = require('cookie-session');
app.use(cookieParser("secret"));
app.use(cookieSession({secret: "secret2"}));
app.use(csrf());
app.use(function (req, res, next) {
res.cookie("XSRF-TOKEN",req.csrfToken());
return next();
});
The req.csrfToken() will generate the csrf token based on the secret stored, in this case, in the req.session.csrfSecret variable, generated by the csrf middleware. Then, it will work.
EDIT: See this issue on GitHub

Resources