Thursday, May 12, 2011

SHA-512 w/ per User Salts is Not Enough


Back in January, I was having a causal conversation about passwords at a local gathering about security and was asked what we use for storing the passwords.

I stated that we are using sha-512 w/ per user salts but we are looking at moving away from this standard to something much stronger.

The response that I received from this person was pretty much in line with other comments I have received and seen on some of our forums.

The two most common responses are: “Oh good, you are using per user salts” and “yeah, using sha-512 is much better than md5.”

Granted, these comments are true, using sha-512 is better than using md5 and better than not using per user salts but there is still a weakness that I feel is overlooked.

Per user salts do provide value, but the problem is that they are typically stored with the hash. So the entry in the database looks something like this:


sha512${salt}${hash}

Or perhaps the hash is stored in a separate column or table and is grabbed as needed during password verification. Either way, both the hash and salt are stored in the same database.

In the event the hash was disclosed or the database was compromised, the attacker will already have one of the two values (i.e. the salt), used to construct the hash.

All the attacker needs to do now is figure out the password entered into the hash formula and the order in which it was used. Order being:


hash = hash_operation({password} + {salt}) or;
hash = hash_operation({salt} + {password})


The two issues with this are:
1) since our source code is public, the order in which we salt is also public and;
2) due to our scale and usability, we store the hash and salt in the same place.


What am I getting at? The real problem is that we shouldn’t be solely replying on hashing algorithms to secure this data. Once the salt is known, it would be pretty trivial to dictionary a bunch of hashes in little or no time and get a pretty significant hit rate. (More to follow on this subject to follow in another post)


What is the solution? Right now the solution is moving away from sha-512 w/ per user salts to something like bcrypt but with a twist.

The twist is adding in a layer of defense plus adding some controls over who can unlock the password equation.

In pseudo code, this is what I mean:


{hmac_result} = hmac_create_operation( user_password + system_shared_password )
{bcrypt_result} = bcrypt_create_operation(( hmac_result + salt ) bcrypt iterations)


The bcrypt result would be stored in the database like this:


bcrypt$bcrypt_result$shared_key_version

The system_shared_password (aka nonce) is not stored within the database. Instead this data is stored on the operating system within a protected file.

Using this configuration a SQL injection vulnerability that provides access to the password hash data would not provide access to the system_shared_password.

Only under the scenario of a full system compromise would both secrets (password hashes, system_shared_password) be compromised.

We feel this solution gives us better controls around who can unlock the hashes and provides a layer of defense around the hashes. We also have put some code together thanks to fwenzel:


Why use per user salts at all? Two reasons, first per user salts ensure that two users with the same password will not have the same hash. Second, the use of salts prevents an attacker from using a precomputed rainbow table against the hashes.

More to come on this subject, as our goal is to increase security and the time in which it would take in order to brute or dictionary the hash. Our goal is and always to provide better protection around authentication systems.

No comments:

Post a Comment