I read about many old questions about this argument, and I thought that the best practice is to set up a cookie with username
,user_id
and a random token.
Same cookie's data is stored in DB at cookie creation, and when users have the cookie they are compared (cookie data, DB data).
Sincerely I can't understand where is the security logic if this is the real best practice.
An attacker who steals the cookie has the same cookie than the original user :|
Forgotten some step? :P
You should store the user_id
and issue a random token in addition to the user's password. Use the token in the cookie and change the token when the password changes. This way, if the user changes their password then the cookie will be invalidated.
This is important if the cookie has been hijacked. It will be invalidated if the user detects the hijacking, and furthermore because the token is unrelated to the password the hijacker won't be able to derive and then change the user's account password and "own" the account (assuming you require the existing password before changing passwords, the hijacker doesn't own the email account so they can't use "Forgot my password" etc).
Take care that the tokens aren't easily guessable (i.e. they should consist of entirely random data, like from a CRNG).
If you want to go one step further, you can encrypt the cookie before sending it and decrypt it upon receipt. And further to that, don't assume that a hijacker doesn't know the encryption key used, so validate the cookie's contents upon decryption.
But all that said, prefer to use a library's persistent session management instead of rolling your own.
You should NEVER EVER store a users password in a cookie, not even if it's hashed!!
Take a look at this blog post:
- Improved Persistent Login Cookie Best Practice (Nov 2006; by bjaspan) (orignal)
Quote:
- When the user successfully logs in with Remember Me checked, a login cookie is issued in addition to the standard session management cookie.[2]
- The login cookie contains the user's username, a series identifier, and a token. The series and token are unguessable random numbers from a suitably large space. All three are stored together in a database table.
- When a non-logged-in user visits the site and presents a login cookie, the username, series, and token are looked up in the database.
- If the triplet is present, the user is considered authenticated. The used token is removed from the database. A new token is generated, stored in database with the username and the same series identifier, and a new login cookie containing all three is issued to the user.
- If the username and series are present but the token does not match, a theft is assumed. The user receives a strongly worded warning and all of the user's remembered sessions are deleted.
- If the username and series are not present, the login cookie is ignored.
I wouldn't even store the username in a cookie, just a random token generated with a near impossible to crack technique and map that to the user in your database, and never store user's password even hashed in a cookie, it will be open to Brute Force Attack. Yes if someone steal the token he can access user's account but the password will not be compromised and the token will be invalidated as soon as the real user logs out. Also remember that you shouldn't allow sensitive tasks like changing password to a user who just have a valid token, you need to ask for the password again for such tasks.
if your cookies are stolen anyone can log into your accounts. it's actually what firesheep does. the security lies in the random token. the whole system assumes cookies can't be stolen. the only other way to get in then is to guess the random token. if you make it long enough it should be nigh-impossible.
The "step" that you seem to be forgetting is that if the cookie value is properly hashed it would be of a little value to an attacker.
EDIT:
Here's a couple of things you can do to protect your users against cookie theft related attacks:
- Regenerate tokens over time, so that an attacker would not be able to impersonate a user unless she has a recent enough cookie. If security is top priority, regenerate tokens on each request (page load). If it isn't, regenerate tokens on password change.
- Keep and validate hashes of user agents, so that an attacker would not be able to impersonate a user unless she has both the cookie and the user agent that of the user.
p.s. Cookies should hold (random) tokens and not password hashes (see Hashes or tokens for "remember me" cookies?).
I always knew that the "remember me" feature only converted the session cookie (i.e. the cookie with the session ID) from expiring when closing the browser to a future date, it doesn't involve saving additional data, only extending the session.
And yes, if an attacker gets the cookie, it can impersonate the user. But this is always valid, and has nothing to do with "remember me".
My approach is the following:
- Hash the
user_id
- Generate an unique key for the user -
md5(current_timestamp)
- Save the key to the DB
- Encode everything so it looks like a BS - base64
- Save it in the cookie
So far, It has been working great for me :)