Do I need a "random salt" once per password or only once per database?

barfoon picture barfoon · Jun 8, 2010 · Viewed 10.1k times · Source

Further to my previous question about salted passwords in PHP/MySQL, I have another question regarding salts.

When someone says "use a random salt" to pre/append to a password, does this mean:

  • Creating a static a 1 time randomly generated string of characters, or
  • Creating a string of characters that changes at random every time a password is created?

If the salt is random for every user and stored along with the hashed password, how is the original salt ever retrieved back for verification?

Answer

Rich Adams picture Rich Adams · Jun 8, 2010

A new salt should be randomly generated for each user and each time they change their password as a minimum. Don't just rely on a site wide salt for example, as that defeats the point of using a salt in the first place.

Using a unique salt for each user is so that if two users have the same password they won't get the same resultant hash. It also means a brute force attack would need to be mounted against each user individually rather then being able to pre-compute a rainbow table for the site.

You then store the result of hashing the salt and password in the database hash(salt + password), along with the salt for each user. You can store these in separate columns, or all in one column (separated by some character not used in the hashes, so ; for example). As long as you can retrieve both you'll be fine.

However, if your database is compromised, either due to someone gaining local access or via SQL injection attacks, then both the salt and final hash will be available, which means a brute force attack on the users' passwords would be trivial. To combat this, as suggested by The Rook you can also use a sitewide secret key stored in a file locally as another input of your hashing method so that an attacker would also need to know this to mount an effective attack. Which means your DB would have to be compromised AND the attacker would need access to local files. So using hash(hash(salt + secret) + password), etc.

While in most algorithms you aim to make things as fast as possible, for password hashing you want to slow it down, this is called Key Strengthening (or sometimes Key Stretching). If it takes 0.00001 seconds for your hash function to return, someone can try brute forcing 100,000 passwords a second until they find a match. If it takes 1 second for your hash function to spit out the result, it's not a big deal as far as someone logging into your application is concerned, but for cracking the password it's a bigger deal since each attempt will now take 1 second to get a result, meaning it would take 100,000 times as long to test each brute forced password than it would using your original hash function.

To make your hash function slower, you just need to run it multiple times. For example, you could do new_hash = salt + password + previous_hash 100,000 times. You may need to adjust the number of iterations to a higher value if it's too quick. If you want to be able to change the value later, make sure to store the number of iterations with the user record so that you don't affect any passwords previous stored.

Your user record should now have a field formatted something like this "$<algorithm>$<iterations>$<salt>$<hash>" (or as separate fields if you want).

When the user enters their password you can retrieve the salt and number-of-iterations from the DB and the sitewide secret from a local file and validate that when you run the same number of iterations with the salt and password, the resulting hash matches what you have stored.

If the user changes their password, then you should generate a new salt.

The hashing method you use doesn't matter (but the hashing algorithm does*). Above I suggested hash(hash(salt + secret) + password) but equally it could be hash(hash(salt) + hash(secret) + hash(password)). The method you use doesn't change the effectiveness of your password storage, one is not really any more secure than the other. Relying on the design of how you hash the password and salt together to provide security is called security through obscurity and should be avoided.

*You should not use MD5 or SHA-1 as these are considered insecure. Use the SHA-2 family instead (SHA256, SHA512, etc). (Ref)