6

In this popular solution for Persistent Login Cookies which involves generating a random 128-bit "token" to be saved in the user's Cookie, Jens Roland recommends:

And DO NOT STORE THE PERSISTENT LOGIN COOKIE (TOKEN) IN YOUR DATABASE, ONLY A HASH OF IT! The login token is Password Equivalent, so if an attacker got his hands on your database, he/she could use the tokens to log in to any account, just as if they were cleartext login-password combinations. Therefore, use strong salted hashing (bcrypt / phpass) when storing persistent login tokens.

But how do you check the Cookie Token against the bcrypted Token in the DB to confirm the Cookie login is valid, when bcrypting the Cookie Token will always yield a different result (since bcrypting always uses a random salt)?

In other words, you can't just bcrypt the Cookie Token and look for a match in the DB since you will never find one, so how do you actually match it against the hashed version in the DB as per the recommended solution ("The server keeps a table of number->username associations, which is looked up to verify the validity of the cookie.")?

Edit:

Keep in mind that as per the recommended solution linked to above, a single user can have multiple Cookies/Tokens for different devices. I mention that because an answer was submitted (that has since been deleted) that assumed it was only one Token per user.

Community
  • 1
  • 1
ProgrammerGirl
  • 3,157
  • 7
  • 45
  • 82

1 Answers1

1

As mentioned in the previous answer, bcrypt stores the random salt as part of the hash, so each token entry in your database will include both random_salt and hashed_token.

When authenticating a 'remember me' login cookie (which should consist of userid and token), you will need to iterate over each of the token entries for that userid (usually just one entry, never more than a handful) and check each one separately using the stored random salt:

foreach (entry in stored_tokens_for_user) {
    if (entry.hashed_token == bcrypt(cookie.token, entry.random_salt))
        return true;
}
return false;

(if your database has built-in support for bcrypt as part of the query syntax, you can create a prepared statement to do this for you)

Jens Roland
  • 27,450
  • 14
  • 82
  • 104
  • 2
    Thanks for the answer, that's what I suspected. However, isn't it complete overkill (not to mention unnecessarily complicated and time-consuming)? Wouldn't simply doing an unsalted 256-bit SHA hash of the 128-bit Cookie Token be more than enough to keep the Tokens safe in the DB, while providing the speed and convenience of direct hash-on-hash matching? I understand why you should never do simple unsalted hashes of user passwords, but how long would it take to actually crack a single unsalted 256-bit SHA hash of a completely-random 128-bit token? – ProgrammerGirl Apr 06 '13 at 15:59
  • @Programmer: You are absolutely correct, and that's actually the solution I sketched out for my own never-coming-out-of-alpha auth library. Since the tokens are randomly generated and the tokens themselves have 'enough' bits, there are no practical attacks relying on either brute force or rainbow tables. The case where it gets tricky is when we are talking about 'remember me' tokens rather than persistent login cookies, since we can't expect a user to type in a random 128-bit token from an email, so in that case we're going to need the salted scheme. – Jens Roland Apr 06 '13 at 21:45
  • 1
    Thanks for confirming. However, I'm confused by what you wrote here: `"The case where it gets tricky is when we are talking about 'remember me' tokens rather than persistent login cookies, since we can't expect a user to type in a random 128-bit token from an email, so in that case we're going to need the salted scheme."` What do you mean? Aren't the Persistent Login Cookies used for the "Remember me" functionality? Why would a user have to type a token from an email to use the "Remember me" feature? Or did you mean "Reset password" tokens? Please clarify. Thanks! – ProgrammerGirl Apr 08 '13 at 11:31