Jason L Causey

Salt and Hash your Passwords

This post was originally created on the A-State CS Department wiki on May 9, 2016 after I observed several students having trouble understanding the fundamental order of operations required to hash and salt a password. The advice here is at the most basic level — do more research before trying this for anything “real”.

The order in which you “salt” and “hash” matters!

The order in which you perform the “salt” and “hash” steps when storing a password is vital to the security of the whole scheme. You absolutely must do things in this order:

  1. Concatenate the ‘salt’ with the raw password.
  2. Hash the salted password.
  3. Compare the hash with the stored hash in the database. (Or, if creating an account, store the hash and the salt into the database.)

Previously, I have seen exam answers where 1 & 2 were reversed. Adding a string of random characters on the end of an already hashed password offers absolutely no advantage. Consider:

Doing it Wrong:

password:  sesame
hash("sesame") => b3fba6554a22fdc16c8e28b173085ccc
salt:             kqrjtiuhvaw

If you hash then salt, you get:

b3fba6554a22fdc16c8e28b173085ccckqrjtiuhvaw

But, if I know that you are using a particular hash algorithm (md5 here), I know the length of the hash string (32 for md5), so I just split it:

                                  |
b3fba6554a22fdc16c8e28b173085ccc  |  kqrjtiuhvaw
                                  |

And throw away the salt… Leaving the hash b3fba6554a22fdc16c8e28b173085ccc that I will just look up in my rainbow table:

286755fad04869ca523320acce0dc6a4 : password
f447b20a7fcbf53a5d5be013ea0b15af : 123456
2f548f61bd37f628077e552ae1537be2 : monkey
b3fba6554a22fdc16c8e28b173085ccc : sesame
6341e21206c4672f8b86dc4af593c5dd : abc123456

I’ll know your password is “sesame” in no time. The salt didn’t help at all.

Doing it Right:

password:        sesame
salt:            kqrjtiuhvaw
salted password: sesamekqrjtiuhvaw
hash("sesamekqrjtiuhvaw"): f429f37d8fe81d46ae1afccf80ccaa88

Now you store the salted-then-hashed password in the database along with the salt:

f429f37d8fe81d46ae1afccf80ccaa88:kqrjtiuhvaw

And if I steal that password, I can try my rainbow table, but the md5 hash for “sesame” is b3fba6554a22fdc16c8e28b173085ccc not f429f37d8fe81d46ae1afccf80ccaa88, so I won’t “see” your password in the table. I would have to brute force every combination of password + "kqrjtiuhvaw" to find one that matched….

"a"  + "kqrjtiuhvaw"  => ee70626aab64bb600e05c4c28c822f0a
"b"  + "kqrjtiuhvaw"  => c13bd0e308c5ef7035fc1ba7409fce14
"c"  + "kqrjtiuhvaw"  => bbdec97b8be3a1098c08ff7ced3c7965
   ...
"z"  + "kqrjtiuhvaw"  => 9c67c13058e8b3713d8f307fe9a914e4
"aa" + "kqrjtiuhvaw"  => b1eab1930d25f46ac92ea8a73fbdc6f6
   ...

I’m going to be at it a while – and then I only get one password for my trouble. Since the salt is unique per-user, I have to start all over again for the next one. Not worth it.

This is why you salt before hashing. The order makes a big difference.

(Also, don’t actually use md5! Look for a proper password hashing function/library, and consult the OWASP password storage cheat-sheet.)