How and when do I generate a Node/Express cookie secret? - node.js

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

Related

what if I have session id of some user? [duplicate]

I'm not exactly sure how the $_SESSION work in PHP. I assume it is a cookie on the browser matched up with an unique key on the server. Is it possible to fake that and by pass logins that only uses sessions to identify the user.
If $_SESSION doesn't work like that, can someone potentially fake cookies and bypass logins?
Yes.
The only thing identifying a user is a pseudo-random value being sent along with each request.
If an attacker can guess the right values to send, he can pose as somebody else.
There are different ways to make this harder:
make session ids longer (more entropy, harder to guess)
check additional information like the user agent (essentially more entropy)
obviously: use a good random number generator
expire sessions sooner to give a smaller set of valid session ids at any one time
renew session ids often, even for valid ids
use SSL to encrypt all communication to avoid outright cookie hijacking
Sessions in PHP by default store the data in a file on the server (/tmp/) and store an identifier cookie usually PHPSESSID (it will be a hexadecimal number, e.g. f00f8c6e83cf2b9fe5a30878de8c3741).
If you have someone else's identifier, then you could in theory use their session.
However, most sites check to ensure the user agent is consistent and also regenerate the session identiifer every handful of requests, to mitigate this.
As for guessing a session, it's possible, but extremely unlikely. It'd be easier to guess credit card numbers (smaller pool of characters (0-9 over 0-9a-f) and a checksum to validate it). Though of course you'd also need the expiry and security code.
Properly implemented, session ids are very long and random enough to make guessing unfeasible (though if you were able to guess a particular user's session id then yes you would be acting as that user). However you can sniff and hijack sessions -- this is what firesheep does: http://en.wikipedia.org/wiki/Firesheep

Should session ID cookie be signed?

I was using the Node library https://github.com/expressjs/session and noticed that it requires a secret to be specified for signing the session ID cookie.
If just the session ID is being stored in the cookie, and not any data, what is the use in signing the cookie?
My reasoning is that if someone wanted to modify a session ID cookie, then that's fine if the session ID sufficiently long to prevent brute force attacks. Am I missing something?
I questioned this as well and the only answers I could think of were,
a) If someone (for no good reason?) changes the default session ID
generation to a non-cryptographically random session ID, signing it
helps prevent attackers generating valid session IDs.
b) Reduce round trips to the session store by validating the session
ID using the signature/HMAC. (I can only imagine this being a problem
for DoS attacks).
Both seem like valid reasons though using good session caching would negate the need for b) and assuming your library's users are daft enough to change the default session ID generation, it seems a bit much to safe-guard against their own stupidity when the majority of users would just use the default generation algorithm.
If you store just id of session there is not reason to encrypt it. You are right.
You need to encrypt if you store some session data inside cookie, not only id.
This will prevent users of changing session data.
A session_id cookie is anything (number or alphanumeric) which identifies a client to a server, which in turns stores (usually temporary) data on the server accessed through this session_id/key.
The point is, if the cookie is going to be passed forth and back over HTTP, it doesn't matter whether you have "signed" it or not. Any man-in-the-middle could get your "signed/encrypted session_id" and make further requests pretending be the proper user. And I mean, this attacker doesn't have to care which information is inside the encrypted data, he could pass the exactly same signed/encrypted data, and the server couldn't figure out if it comes really from the right user.
In cases like these, you have to figure out if the privacy is important, if so, don't think too much, you got have to use HTTPS. If you understand it is not so important, don't waste your "processing time" signing/encrypting and decrypting your session id.
This answer is valid only for signed session_id.
I believe the reason it is signed is so that it is not easily guessable. For instance if someone decided to use integers as session id's, you could easily impersonate another user's session by trying multiple numbers. I believe that the signing makes it so that changing your session id value client side will not be valid (that is it prevents tampering).

What should I use for express.cookieParser() secret?

The docs say it should be secret, but my code is published on github.
Would app.use(express.cookieParser(crypto.randomBytes(64).toString())) work, or should the secret be the same when the server restarts? Should I store the secret on disk? How secret does it need to be?
To keep your secret secret, you can set it in an environment variable (called 'COOKIE_SECRET' for example) and then you can do:
var cookieSecret = process.env.COOKIE_SECRET;
app.use(express.cookieParser( cookieSecret ));
(Or if you would like a more sophisticated config setup, you might like to take a look at nconf. It unifies configuration across environment variables, command-line arguments and flat files).
It should be the same if you want to keep sessions after a restart.
The secret is used to validate the session data on server to prevent malformed cookie data.
Maybe you can write your random data into a file and read the secret from file on app start and if file exists you dont create a new random key.
Secret is used to parse and match session cookie. If you are changing it after restart, then it will make previous sessions void as cookie will not be valid with new secret.
Still, in case if cookies were stolen, you might consider of changing secret, which sort of protects you. It is not good practice to store secret anywhere apart of place where it is needed. Same with any secrets and salts, as access to them is not good for your security.

HTTP Remember me authentication

I'm trying to write a simple HTTP remember me authentication system for users.
My users could be represented as such
{
"email" : "foo#bar.com",
"password" : "8EC41F4334C1B9615F930270A4F8BBC2F5A2FCD3" // sha1 hash of password
}
So my idea is that I need to create a cookie, with indefinite (really long) expiration time, that will hold some type of information to enable me to fetch the user from the database, therefore logging the user in.
My first idea was to just simply store the email:password string as a cookie. I thought this would be good since nobody else can really generate that type of information other than the user itself, and I could retrieve the user quite easily by simply comparing the username and password based on what's in the database.
However then I thought this wasn't really good. It turns the password digest into, effectively, a second password that's stored in the clear and passed over the wire in every request.
So then I thought maybe I could generate a signature each time the user logs in, which would basically be a random hash that is stored directly in the user object in the database.
The user logs in, it generates this signature that is stored, and the cookie holds this signature. Whenever you access the website, the site checks which user has that particular signature and logs the user in. Logging out will effectively erase the cookie, and new logins will generate a new random signature.
Does this approach take into account any other vulnerabilities?
I know, I should probably use a library already made for this, but this is just simply an exercise in web-security.
This is essentially what most sites do when you log in. Yes, the cookie should hold a unique identifier for the user's "session". The cookie should be essentially random. Up to you whether to make it persistent across browser sessions.
Along with the cookie in your authentication DB, also store a timestamp of when the entry was created. Cookies older than N seconds should be considered invalid (set N to your taste). You can reset the timestamp each time the cookie is used so that idle sessions time out.
Note that the same user may want to have multiple sessions (do you ever log in to your Email account from both home and work?), so the concept here really is "session", not user.
Vulnerability point-of-view both are same! Cookie stealing and related mechanisms however browsers are smart enough now so you shouldn't worry about that.
Second approach is good in terms of privacy as well since it does not includes email address in the cookie. And it seems much more similar to like storing the sessionID which in your case you are generating a random hash and storing it in DB.
But i think it would be more wiser to use the first approach; you can add another layer to the digest and encrypt it with your some algo or private key; to be on safer side.

What is the session's "secret" option?

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?
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.
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
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:
It protects users from themselves
It adds an extra layer of defense
(With a custom session ID generator) It allows detecting malicious behavior
(With a custom session ID generator) It allows bundling a timestamp into the ID
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!

Resources