The current advice for storing passwords is to store them salted hashed. Not just hashed, but hashed after salting. Sam showed why that’s so when he explained Rainbow cracking in January 2006. Briefly, a dictionary of pre-computed hashes allows you to reverse a hashed password. But you would need a custom pre-computed dictionary to break a password that’s salted and then hashed. If that salt also varies from user to user, it becomes almost impossible to crack a database of hashed passwords.
All that sounds nice and warm. But storing passwords salted hashed makes it more tricky to transmit passwords.
First, let’s review Rule #1 of transmitting passwords. “Do not send password hashes. But, send a salted hash of the password.” Why? Since a password always hashes to the same value, it’s possible to replay a hash that’s been captured earlier. So, adding a different salt each time protects the transmission from a replay attack.
Trouble brews when the password is stored salted hashed - not just hashed but salted hashed with a different salt for each user.
Let’s call the user-specific salt used to store the password “user_salt”. This is a random string that’s different for each user, but is static for a given user.
So what’s stored in the database is Hash(password, user_salt)
To follow Rule #1 of transmitting passwords, the client needs to hash the password with per-session salt, one that changes every session. Let’s call this the “session_salt”. Obviously, the user_salt and session_salt have to be different – notice their different lifetimes.
Thus, when the user types in the password, the client should send the server Hash(password, session_salt).
Wait a minute. The server does not store the hash of the password. It only stores Hash(password, user_salt).
That means the client will have to first calculate Hash(password, user_salt), and then hash it with the session_salt before sending to the server. Now, since the server knows the session_salt, the user_salt and the salted hash it can check if the user entered the right password.
Hash ( Hash ( password, user_salt ), session_salt )
It’s a bit confusing, but you have it there. The password the user types is first salted and hashed with the user-specific salt. Then it’s re-hashed with the session-specific salt. The string that’s sent to the server is thus resilient to replay attacks, as well as pre-computed dictionary attacks. Phew!
And when you just thought you were done, along comes a boomerang. Er, how do you send the user specific salt in the login page, even before you know who the user is?
Notice that the client uses the user-specific salt to hash the password before sending. That requires the client to know who the user is. But this is the login page, and one doesn’t know the user yet.
Well, there are two ways to solve this:
1. Split the login process across two pages: in the first page, just ask for the username. In the second page, ask for the password and then do all these salted hash calculations. Now the app knows the username before the second page is served, so it can tell the page what the user-specific salt is. This works, but the downside is it makes the simple act of logging in a 2-stage process.