Node.js. Encrypted vs Signed cookies - node.js

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)

Related

Express session, how do I get the session after posting? I see the session ID in browser under cookies, but it's undefined in request object?

Sorry guys, I'm really new to sessions and cookies and I'm trying to understand the mechanism behind it. I wanted to add register/login to my simple website and in order to do I need to understand web authentication and I really think I will have tons of questions regarding this topic.
Initially, I have register page that sends info after clicking submit button to a node server using express.
I'm trying to see what happens, so I've created a session in post route, it's created in the browser (connect.sid), then I commented out the part that creates that session and just tries to redisplay the session object, but it's undefined, but I still can see the session in the browser's cookies section, so what's going on? Thanks
app.use(session({
secret:"my test secret",
cookie:{},
resave:false,
saveUninitialized:false
}))
app.post("/register", (req, res) => {
req.session.usertest = "testsession_hardcodedvaluefornow";
console.log(req.session.usertest); // -> this is okay when above line to create is uncommented
//but when I comment the session assignment, it becomes undefined?
res.send("In register...");
})
I can see the session cookie even after commenting out the create session and posting over and over.
connect.sid s%3A_TahsTv0xhY-iHIdjDRblYJ_aZZ5oiSd.do7JcOGR1FaXPcFFIQ6hg5AW%2B0XVsYwIRO8vndyjDzs
req.session.id produces a different value (not undefined) even if I delete my session in the browser, so not sure where that comes from.
There is no "usertest" key in the session object, therefore it is undefined. The reason it's not undefined when you uncomment that line is because you create that key yourself in that instant with that line.
You can get the whole session object by using req.session, the session id by using req.session.id and the session cookie by using req.session.cookie.
Edit
To further clarify: a session will be made for every connected client. That is why you can see the cookie in the browser. That has no meaning however, it's just a way to uniquely identify that client (without actually telling you who that client is). Any information about that session (whether they're logged in, user id,...) needs to be stored in a session store. Express-session will use memory store by default, if the server restarts all that information will be lost, which is why that key doesn't exist. In order to preserve that information it has to be stored in a persistent session store. More information on session store implementations for express-session and how to use them can be found here: https://www.npmjs.com/package/express-session
As for the cookie values you get, those are the default ones set by express-session since you haven't set any yourself. You can change the maxAge to let it expire (or set it so 10 years or more for a more persistent session), you can specify a domain to which that cookie belongs, you can set it to secure (to only allow it over secure connections, e.g. https) and httpOpnly is by default true. httpOnly cookies cannot be accessed/altered by the client (although you can see them in the browser), do not set this to false.

Why use cookie-session in addition to passport.js?

My understanding of passport.js so far is that passport.js serializes the user object and sends an ID every time to the client. I am just starting with it so sorry if it's a silly question:
Instead of express-session, I am using cookie-session as I am a beginner. My understanding of cookie-session is that it sends a session ID every time, and this ID can be used to look up the database when needed.
Now, I don't understand why we can't just use the passport.js ID? Why do we need to use cookie-session in addition? Also, (this may be a little unrelated, but) is the difference between session-based authentication and token-based authentication that this ID that's shared is dynamic, or changing every time? Is this still the standard and modern way of doing it in 2020?
"Instead of express-session, I am using cookie-session as I am a beginner."
using cookie session does not make anyone beginner. If you are going to store large data, use express-session, cause it stores the data in the database or redis, keeps the database id of that data, so when it gets a request, fetch the database with that id and compare the request credentials. On the other hand, cookie-session stores the data upto 4kb in the cookie on the user browser and since only user-id is stored in the cookie with passport.js, generally cookie session is used.
passport.serializeUser(
(user, done ) => {
done(null, user.id); // stores the id<4kb
}
);
When client authorizes your app, google send the responds to your callback url.
app.get("/auth/google/callback", passport.authenticate("google"))
passport.authenticate() will call req.login() this is where passport.user gets generated. req.login() will initiate the serializeUser() which determines which data of the user should be stored in the session.
passport:{user:userId}
Now this object will be assigned to req.session. so we will have req.session.passport.user
Everytime when you make a request to a server, browser automatically checks if there is cookie set related to that server and if there is it automatically attaches the cookie to the request. If you were using token based authentication, you had to manually attach the cookie to request everytime you make a request. Cookie is just transportation medium, you store data and move the data, you can even store the token in cookie. Cookie is not just related to authentication. If you have server-side project, you have to set cookie.(this is just a side node).
"My understanding of cookie-session is that it sends a session ID every time, and this ID can be used to look up the database when needed."
so far I explained how session is created. Now what happens when user makes a request?. In app.js file you should have two middleares.
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.initialize()) this function checks if req.session.passport.user exists, if it does it will call passport.session(). if it finds a serialized user object in the session, it will consider this req is authenticated. And then deserializeUser() will be invoked. it will retrieve the user and attach it to req.user
You don't need to use session. It is totally upto you. Just put {session: false} in route. You don't need to write passport.serializeUser and passport.deserializeUser.
cookie-session puts cookie on client system, and it is sent each time with request. passportjs search that cookie and run deserializeUser to convert it into object and attach it with request object.
express-session stores session data on the server; it only saves the session identifier in the cookie, not session data.
where as cookie-session is basically used for lightweight session applications. it allows you to store the session data in a cookie but within the client [browser]. Only use it when session data is relatively small and easily encoded as primitive values See this question for more understanding
const express = require('express');
const { Router } = express;
const router = new Router();
router
.get('/', passport.authenticate('google', { session: false }))

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: 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

Using cookieParser() and cookieSession() together?

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.

Resources