3

Reading this excellent answer about password hashing and wondering how to implement it:

The Wicked Flea wrote:

Generate a nonce for each user; this alone defeats the rainbow table. This is a random number that, depending on the range, expands how many resulting hashes there are.

So beside users' password store a unique token in my database?

The example code in the original post:

function hash_password($password, $nonce) {
  global $site_key;
  return hash_hmac('sha512', $password . $nonce, $site_key);
}

How can i verify a password with this code? Let me explain:

When user submits his password i need to generate it's hash to check for an existing database row where email address and hashed password match. How can i select this row when i know nothing about users' $nonce? Am i missing something? Maybe i need to select user by only his e-mail address then verify the password hash later?

Btw, do you recommend this hashing method?

Community
  • 1
  • 1
fabrik
  • 13,237
  • 8
  • 54
  • 69
  • You do not need to select the user using the password as a selection criterion. Just select the user based on a unique characteristic like e-mail address or login, then check the password afterwards. Also see a related question for some hashing ideas (disclaimer: I answered on that): http://stackoverflow.com/questions/3787346/email-address-as-password-salt/3787414#3787414 – Arc Apr 07 '11 at 10:27
  • 1
    Btw, selecting by username only also has the advantage that you can display "user does not exist" or "password invalid" depending on the error which *extremely* increases usability of your website. Sometimes you had to use a different username for whatever reason and after long time you might try the old one and tons of passwords because it just says "login failed". With a "user does not exist" error this would not be necessary – ThiefMaster Apr 07 '11 at 10:33
  • @Archimedix Thank you for your reply. Btw, sorry but i can't see the difference between adding the system-wide salt to the clear text password or to the key. Can you shed the light on it? – fabrik Apr 07 '11 at 10:48
  • I'm no crypto-expert by any means but seeing that your salt is what you would consider your secret key to encrypt the user's password (secret to the user, that is), I would only use the salt in the HMAC key and put the password in the message part. That way, the user has no influence on the HMAC key to run differential attacks. – Arc Apr 08 '11 at 10:38
  • 3
    And yes, the usability enhancement "user does not exist" is a security flaw as Laas rightly said as it enables an attacker do compose a list of valid user accounts. This is where you can distinguish sites with bad security from those with *maybe* not-so-bad security. However, timing-based attacks can still be used to determine which accounts may be valid, however with limited confidence - for a site with many active users it is hard to say whether different loading times are caused by retrieval of non-existent users or a bad password. – Arc Apr 08 '11 at 11:39

1 Answers1

3

I think you have nailed the idea. The same concept is applied in general UNIX-style salted passwords - store salt in clear text with password and retrieve it by username, then use the salt and provided password to produce new hash to be compared to the stored value.

It is up to you to consider wether you trust your DB server (and the connection to it) to use a hashing algorithm that is supported by the DB and let the DB do the math:

SELECT * FROM users WHERE email = 'email' AND password = SHA1(CONCAT('cleartextpass',nonce));

Or you could do the math in the code after retrieving all the matching emails.

EDIT: The ThiefMaster comment on differentiating between missing user and invalid password is classical security flaw, which allows attackers to acquire a list of valid usernames and concentrate on breaking their passwords instead of fishing in the darkness. I would strongly recommend against it.

Laas
  • 5,692
  • 29
  • 50
  • can you explain your answer a little bit more? It's not fully clear where `nonce` comes from in your query. Basically your solution looks better for me because it can be done with a database query only. – fabrik Apr 07 '11 at 10:52
  • In the SQL query the _nonce_ is the name of a table column (notice, it is not quoted). This means that the CONCAT() takes the clear text password and appends to it what ever is stored in the field _nonce_ – Laas Apr 07 '11 at 11:01
  • and i can generate the SHA1 key in SQL and PHP too, am i right? Can i use this *nonce* field for other purposes? Lost password token, etc? Or this would be a security flaw? – fabrik Apr 07 '11 at 11:07
  • @fabrik - (1) yes, you can generate the hashes in either, as long as both support the same algorithm (e.g. MySQL has no HMAC). (2) This is two-fold: the purpose of the nonce is to acquire different hashes for identical passwords. As such, knowing nonce, an attacker can reduce the pool of strings which need to be looked through, when brute forcing. On the other hand - the attacker can only benefit from the nonce when it has the actual password hash, in which case he probably has the nonce anyway (from SQL dump, etc). HMAC mitigates all of this by introducing site_key. – Laas Apr 07 '11 at 11:41
  • 1
    My last comment should not be read that I recommend sending out the nonce - you're always safer, the less the attacker knows. – Laas Apr 07 '11 at 11:42