Understanding when and how to Salt your password hashes.

Watched a quick presentation today on Salting and Hashing. And while the conclusions were generally correct, there was a lack of understanding in some of the rationale.

The first thing to understand is WHY we salt passwords in the first place. And that is, generally, to stop hash table attacks. A hash table attack is basically a hacker generating a bunch of hashed data and using that to reverse engineer a password (or a valid password "substitute" in the case of collisions).

Basically, if I make a password "123" and it gets hashed and stored as "XYZ", then if there is no salt or any other differentiator in the password hashing process, if another user creates a password of "123", then their hash will be identical to mine.

In a hash table attack, an attacker generally has access to the database. Then, all they need is a way to generate hashes. Typically by creating accounts or updating passwords on an existing account. And then you simply use normal brute force password cracking methods. Try random passwords, compare the hashes. If you get a match, then you have a password you can use to access that account.

When you salt, you prepend or append some extra data before hashing. And you generally try to make it unique per entry. A static salt is no better than no salt when it comes to hash table attacks. What you're looking for is a way to ensure that if I use "123" as a password and you use "123" as a password that they both result in unique hashes. Then, even if you have access to the DB, you STILL can't tell when/if you have hit upon a valid password for another user, even if the hashes are identical.

Because of this, it doesn't really matter how the salt is determined, or even where it is stored. In fact, many systems will store the salt in plain text in the DB right next to the password.

Now... when and how to salt also depends a lot on circumstances. And this is actually what a lot of companies get wrong. If ALL of your data is stored in one DB or secured by the same set of credentials then it likely doesn't matter. Remember, hash table attacks are only useful if you can:

  1. Generate your own hashed values
  2. Compare your results to others
And that means that a user has some level of access to the system and likely has access to the DB. If they have access to the DB, then they have network access and passwords to access the DB. So, if all data is in one place or secured by the same credentials then they likely have access to the entire database. And if they have the will and access to the DB, they can probably do whatever they want with enough time. 

So, keep that in mind; if you're actually interested in security, don't JUST salt your passwords. Also, keep authentication data separate from the rest of your application data.

Other things to keep in mind; are you encrypting data at rest? If so, are the salts and algorithms being stored in the same place as the encrypted values? If so, then you have secured nothing. 

Another consideration is whether or not attackers have an attack vector to generate hashes. The one case where a static salt makes sense is if you're just encrypting data at rest, the tooling which does the encryption isn't publicly available and the salt is stored separately.

When it does matter, and you're properly separating your data, then you want the salt to either:
  • Be random or unique
  • Sufficiently different from record to record and outside the influence of the end user
    • (IE - Don't use an answer to a security question, or a small pool of values)
It shouldn't matter if the salt is stored in plain text. If it matters, you've done something wrong. 

The final thing to consider, and one that I fear a great many people overlook is; the complexity of your hashing algorithm needs to be greater than the complexity of your passwords. While this isn't specific to salts, if the hash algorithm is not unique enough you could have collisions, which is to say, multiple different password values which have the same hash. Because, at the end of the day, when you hash and salt, what you're validating is NOT the password, but the HASH of the password. 

So, if "ABC" hashes to "XYZ" and "!@#" also hashes to "XYZ", then "!@#" will be treated as the correct password. 

And when I say "the complexity of your passwords", I really mean the complexity of the password PLUS the salt. Some people have a tendency to use really long, random salts thinking that they somehow make things more secure (and technically they do, but beyond a point it probably doesn't matter). 

If you're combining that with an already quite strong password policy you increase the odds that there are fewer possible unique hash "buckets" than possible unique passwords when factoring the salt in. Ideally, you want at least a few orders of magnitude more head room in your hash complexity than in your password complexity to decrease the odds of a collision. 

For example, a SHA-256 hash has 2^256 possible hash values. So long as your password complexity + salt is less than that, there is only a POSSIBILITY of a hash collision. And the lower the number of possible unique passwords, the lower the odds of a collision. BUT, if you have a password+salt complexity which can yield MORE than 2^256 combinations then there MUST be collisions in that set of possible passwords. 

So, you really need to balance your priorities. If you NEED either really complex passwords or long hashes, then you may need to use SHA-384 or SHA-512. They are slower, but they are also offer much more unique head room. As I said before, you really want to know many unique password combinations your password policy can permit. Then you want to make sure that after you factor in the salt that you end up with a maximum number of permutations substantially lower than the number of unique hash values for your chosen algorithm. 

After all, hash collisions kind of defeat the purpose of enforcing password complexity rules. 

Comments

Popular Posts