129

I don't know anything about cryptography. I'm wondering what the session secret is.

I see code like this:

app.use(express.session({
  store: mongoStore({
    url: app.set('db-uri')
  }),
  secret: 'topsecret'
}));

What is the secret and should I change it?

Harry
  • 52,711
  • 71
  • 177
  • 261

3 Answers3

95

Yes, you should change it. A session secret in connect is simply used to compute the hash. Without the string, access to the session would essentially be "denied". Take a look at the connect docs, that should help a little bit.

maikthomas
  • 427
  • 5
  • 15
Hacknightly
  • 5,109
  • 1
  • 26
  • 27
  • 46
    Not only can you change it, but you should change it. – Michael Mior Sep 03 '12 at 02:29
  • 3
    @MichaelMior, how often? – FRD Jul 01 '14 at 11:23
  • 12
    @FRD My point was that you shouldn't keep the default of something like `topsecret`. The secret should be a random string of characters. Ideally you would also change it periodically in case it has been discovered. However, this requires support for secret rotation so you don't immediately invalidate existing sessions. That is, two session secrets should be considered valid simultaneously. To my knowledge, Express doesn't have support for rotating secrets at this time. – Michael Mior Jul 02 '14 at 14:00
  • 11
    @MichaelMior express-session since 1.11.0 supports secret rotation. From the docs: "If an array of secrets is provided, only the first element will be used to sign the session ID cookie, while all the elements will be considered when verifying the signature in requests." (See [expressjs/session#127](https://github.com/expressjs/session/issues/127) for details.) – Wolfgang Jan 04 '16 at 01:20
  • Is this same thing as signed cookie, if yes.. do you need to do this: `res.cookie('name', 'value', {signed: true})` – Muhammad Umer Feb 24 '18 at 23:04
  • 4
    Two questions were asked here: "what is the secret" and "should I change it". Only the latter question was answered. – Alexandre Dec 19 '19 at 20:33
  • The secret in this library is pointless. You might as well have made the token longer and achieved the same level of security. That being said, this library doesn't support rotation of these keys in a "true" sense.. if an attacker gets one of these keys, then they can still use that stolen key since that key will be somewhere in the array of secret keys that is used to verify the signature - unless the developer decides to remove that key from the array in which case all users that have a session signed with that key will be logged out. – Rishabh Poddar Jan 20 '20 at 10:31
  • 1
    It's not pointless, it prevents your users from changing the cookie however they wish. That's a very good idea! – Brecht De Rooms May 27 '20 at 10:49
  • 2
    @RishabhPoddar The frequency of rotation and the depth of the array can be tuned to reach a compromise. Say the key changes daily, and the array length is 5. Then you're allowing an attacker with a key access for a maximum of 5 days. Also, when the last key rotates, you're only invalidating sessions that are already 5 days old (probably not too big of a problem, since sessions are likely being refreshed). Key reset time adjusts the granularity of whose sessions are being invalidated. Array length adjusts number of keys in use at at time. Together they adjust the amount of time a key is valid. – nullromo Jan 05 '21 at 20:12
  • 2
    also, note that you should **avoid hard-coding the secret in the source code**! Instead, you would commonly set it as an environment variable and access it via e.g. `process.env.SESSION_SECRET` ([see this comment on GitHub](https://github.com/expressjs/session/issues/524#issuecomment-346570923)) – Marko Knöbl Jan 21 '21 at 11:15
23

The secret is used to hash the session with HMAC:

https://github.com/senchalabs/connect/blob/master/lib/middleware/session.js#L256

The session is then protected against session hijacking by checking the fingerprint against the hash with the secret:

https://github.com/senchalabs/connect/blob/master/lib/middleware/session.js#L281-L287

substack
  • 3,346
  • 24
  • 14
  • 4
    How does the hash protect against session hijacking, though? If the attacker has the session cookie, then don't they have the hash as well? If the attacker *doesn't* have the cookie, then how does the secret hash make it any harder to guess (versus just having a longer random session ID)? – Stuart P. Bentley May 23 '13 at 18:18
  • 11
    the secret is a salt for the hash, this just makes it harder for someone to: 1. decrypt a phished cookie, 2. spoof a session by impersonating a user, since they don't have the secret (salt) they can't produce a proper session id. – mattdlockyer May 31 '13 at 22:41
  • 7
    Both those links are invalid on account of this being a 3-year-old answer. – trysis Aug 21 '14 at 14:07
  • 6
    @mattdlockyer This does not protect you against session hijacking. And why would an attacker try to decrypt a cookie? If the attacker has the cookie then he/she already has full access until the session ends. I mean it's not like people store their user's password in the cookie, that would be ridiculous. And things like jwt should almost always be avoided. – Forivin Sep 19 '17 at 14:10
  • There seems to be mixed answers regarding the purpose of a session's secret. Some say it protects against session hijacking while others disagree. Can someone explain what is the real purpose of generating this hash? Precisely, what is it that it is computing a hash for? – nawK Apr 04 '19 at 03:12
  • It is definitely not used to prevent session hijacking. Session hijacking can theoretically never be prevented. It can only be detected.. I really don't see the point in hashing the SID.. Since SID itself is a long, unique string.. – Rishabh Poddar Jul 24 '19 at 17:57
  • 1
    I'm confused by many of these answers. As far as I know in express-session it's meant for signing the cookie. Signing is a cryptographic method to make sure that nobody can just change it. If someone would change the cookie you can detect it and the cookie would no longer be valid. Is this useful? Depends on what you store. If you store the user id in such a cookie (some do, although I think that's a bad idea) and someone can guess the user id of someone else, yes than this is very useful. It's a good measure in general though to make sure people can't change the cookie. – Brecht De Rooms May 27 '20 at 10:48
  • I can hardly believe there's so much confusion as to why signing is necessary. It simply prevents the very well known session fixation attack (a flavor of session hijacking). https://en.wikipedia.org/wiki/Session_fixation – java-addict301 Dec 01 '20 at 21:07
  • @RishabhPoddar you are wrong - session hijacking can be prevented (and hashing specifically helps mitigate session fixation attack). – java-addict301 Dec 01 '20 at 21:10
  • 1
    @java-addict301 session hijacking can occur via social engineering or via some malware that the user downloads on their computer, or via a rogue browser extension, or via a rogue third party JS dependency.. all these methods are very difficult to prevent from the point of view of the application developer. That's why I said it's impossible to prevent it upfront, but can be detected and mitigated. – Rishabh Poddar Dec 03 '20 at 05:56
  • I would love for someone to explain how hashing the sessionID prevents session hijacking. It simple creates the same scenario as you had before (a unique string to identify the user). If some were to then steal that 'hash' and plug it in, the same thing would happen as if it was just a standard uuid. – hayhay Dec 23 '20 at 19:21
  • For the record, I tested if an old forgotten sessionID could be used: Nope, it generates a new random one. So no fixation issue. The only reason is making random guessing harder... See https://github.com/expressjs/session/issues/889#issuecomment-1117589599 – Pascal Pixel Rigaux Jun 22 '22 at 17:08
13

Motivation for this answer

The other answers address "Should I change it?" and provide a surface-level explanation on "What is it?" As someone who just started using express-session, I was curious and in my reading found much disagreement over whether having a secret like this is valuable and how much.

Many people discussing this topic seem to be security novices like myself. However, I came across this answer with a comprehensive explanation of the intended effect of the secret and some of the possibilities. You should read the whole answer, but I will try to summarize.

What does the secret protect against?

The type of attack in question here is session hijacking. Typically, this involves the attacker acquiring the session ID of a valid user, thereby being able to emulate that user's session and allowing the attacker to access information or act on the victim's behalf.

How can you protect against session hijacking?

A good start is to use a session ID that is suffienciently long and random, as it inhibits an attacker's ability to guess the ID. As noted by the other answer's author:

It is also critical that session ids are not generated using a predictable algorithm such as a counter because if such logic exists, the attacker is no longer guessing but generating session ids.

As an example: if an attacker finds out that your session IDs are sequential (e.g. 1, 2, 3), then if they discover a session ID of 2 then they can reasonably assume 1 and 3 are session IDs as well.

What does express-session's secret do?

The Express session middleware...calculates a hash over the combination of the session id and a secret. Since calculating the hash requires possession of the secret, an attacker will not be able to generate valid session ids without guessing the secret (or just trying to guess the hash).

So the secret is used to create a hash that is long and random. If the session ID is already sufficiently long and random, then using a secret in this manner is largely redundant. As other users have pointed out, at the end of the day, the attacker is just guessing one long and random instead of another.

But don't be so quick to dismiss the use of hashing!

express-session is a public package

An important feature of the Express session middleware is its support for user-generated session ids. This allows developer to deploy the middleware in an existing environment where session ids are generated by an existing entity which might reside on a completely different platform. Without adding a hash to the user-provided session ids, the burden of building a secure system moves from the expert (the module author) to the user (which is likely to be a security novice). Applying a hash is a much better approach than forcing an internal session id generator.

If an inexperienced user instead defines their own insecure session ID generator (e.g. say, something sequential as discussed above), hashing it will ameliorate that security flaw.

As the author notes elsewhere:

Also, this is a generic module assuming as it's core requirement a wide range of users. It absolutely has to assume that some people will use it poorly (e.g. increment ids) and accommodate that. This is also common practice when building modules for a wide audience.

Don't put all your eggs in one basket

Hashing using a secret is one layer of security, and can help cover flaws in other layers. What if your random session ID generator has a bug that can be exploited? What if you accidentally use RNG.pseudoRandomNumber() instead of RNG.strongRandomNumber() when coding? What if one of your dependencies breaks or is compromised? Once again, hashing helps patch those flaws.

There are other benefits

You can detect the difference between expired/unallocated IDs and invalid (maliciously generated) IDs:

By including an integrity component in the session id (via a hash or signature), the server can immediately tell the difference between an expired session, an unallocated session id, and an invalid session. Even if you just log invalid authentication attempts (and you should), you would want to log an expired session differently than an invalid one.

You can build a tamper-resistant timestamp into the ID:

While cookies come with an expiration policy, there is no way to ensure it is actually obeyed. (...) A common best practice is to include a timestamp in every credential issued, which can be as simple as adding a timestamp suffix to the randomly generate session id. However, in order to rely on this timestamp, we must be able to verify it was not tempered with and the way to accomplish that is with a hash or signature. (...) Adding a timestamp to the session id allows the server to quickly handle expired sessions without having to make an expensive database lookup.

You can immediately invalidate many IDs if something goes wrong:

Because generating a hash or signature requires a server-side secret or key, replacing the secret will immediately cause all session ids to fail validation. By using different secrets for different types of session ids, entire classes of sessions can be segregated and managed. Without such a mechanism, the application itself has to make a computed decision about the state of each session or perform mass database updates.

In Conclusion

The having a secret (and using it to hash) provides many benefits:

  1. It protects users from themselves
  2. It adds an extra layer of defense
  3. (With a custom session ID generator) It allows detecting malicious behavior
  4. (With a custom session ID generator) It allows bundling a timestamp into the ID
  5. It provides a kill switch

And once again, I would like to credit this answer for everything in this post. I am just a curious onlooker!

Shadorian
  • 182
  • 1
  • 9