Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How and when do I generate a Node/Express cookie secret?

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.

like image 819
Max Pekarsky Avatar asked Nov 03 '17 22:11

Max Pekarsky


Video Answer


1 Answers

A typical session cookie looks something like this:

s%3Al3ozSdvQ83TtC5RvJ.CibaQoHtaY0H3QOB1kqR8H2A

It'll be longer than this but the format is much the same.

  • The s%3A at the start indicates that it is a signed cookie.
  • The l3ozSdvQ83TtC5RvJ is the session id (you can confirm this by checking req.session.id on the server).
  • The 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.

like image 108
skirtle Avatar answered Oct 12 '22 09:10

skirtle