I'm building a node/express app and am using the express-session
and mongo-connect
modules to store my sessions and persist them on restart.
However, a new cookie ID is still generated every time I restart the server. I've narrowed the problem down to my session secret, which is a randomly generated string of 16 chars Here is my session code:
app.use(session({
secret: dbops.randomString(16), // generate a 16 random char string
saveUninitialized: false,
resave: true,
store: new MongoStore({
db: thisDb,
ttl: 14 * 24 * 60 * 60
})
}));
The issue is with my randomString
function - when I replace it with a static string, my sessions persist through a server restart. What should my cookie secret be? Should I just pick one long randomized string and store in in an ENV variable?
I think I'm generally still confused on the purpose of the string, since my Cookie SID seems to be generated randomly anyway.
A typical session cookie looks something like this:
s%3Al3ozSdvQ83TtC5RvJ.CibaQoHtaY0H3QOB1kqR8H2A
It'll be longer than this but the format is much the same.
s%3A
at the start indicates that it is a signed cookie.l3ozSdvQ83TtC5RvJ
is the session id (you can confirm this by checking req.session.id
on the server).CibaQoHtaY0H3QOB1kqR8H2A
is the signature.You can think of the secret
as a bit like a password used to generate the signature
In general, signing is used to confirm that the text originated from the right place. Someone might be able to tamper with the text but they wouldn't be able to sign it using the correct signature because they don't know the secret
. In the context of cookies the 'origin' for the cookie is the server itself, so it just provides a way to confirm that the cookie that comes back is the same one that was sent.
However, in the context of session ids that doesn't really matter because if someone changes their session cookie it'll just mean they won't be logged in anymore because it won't match the id in the database. So why bother signing them?
Generating a random session id is actually quite difficult. Even if it looks random to you it may still be possible for someone to guess it. Signatures can help to solve this problem: how do we stop someone guessing another user's session id when the 'random' ids aren't very random?
Let's take this to a hypothetical extreme. Rather than using random session ids let's just count up, so the first session has id 1
, the next session would be 2
and so forth. It's easy for someone to guess what the session ids are but that isn't enough to hijack a session. They'd also need to be able to sign it, to get something like this:
s%3A432.D5egYRj1G7sJyfbyB7jDh7Gf
The session id here is 432
and wouldn't be difficult to guess but without knowing the signature a hacker couldn't do anything with that knowledge. So signatures make it difficult to guess the cookie value even if you can guess the 'random' part.
Back to your question about express-session
, as the name suggests the secret
needs to be kept secret. It needs to stay the same between restarts or, as you've noticed, the signatures all become invalid and the old session cookies will all be rejected. It also needs to be the same between Nodes in a cluster as there's no guarantee that requests will always go to the same Node.
You should also be aware of the keys
setting that can be used instead of secret
. Using keys
allows you to change the secret
used to generate signatures without immediately invalidating all the existing sessions. The idea is that you specify an array of keys (secrets). Only the first one would be used for generating the signatures but all the entries would be valid for checking an incoming signature on a cookie. The idea is simply that old secrets can be included in the array for as long as they're needed and then they can be removed once we're confident that no sessions are using them.
Should I just pick one long randomized string and store it in an ENV variable?
Pretty much. It doesn't have to be crazily long but it does need to be difficult for someone to guess. It's a bit like a password but with the advantage that you don't have to be able to remember it. Ideally you'd keep the secret
used in production out of the code and using an environment variable would be one way to achieve that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With