0

I'm asking this question to have a professional opinion about this, and to check if my assumptions are correct. The question derives from few comments I exchanged on SO: Problem with login I can login with any username and password

Take these three different methods to 'secure' a password in your DB (pseudocode):

One:

global_salt = 'a salt string shared by your system and persistent in time'
hashed_password = hash( global_salt + password )
save(username, hashed_password)

Two:

salt = generate_random_salt()
hashed_password = hash( salt + password )
save(username, salt, hashed_password)

Three:

global_salt = 'a salt string shared by your system and persistent in time'
hashed_password = hash( global_salt + username + password )
save(username, hashed_password)

Now it is clear that using a salt in the first place is wise to prevent weak password reconstruction by the aid of a hash lookup table.

But let's assume that we have picked a random global_salt that has never changed and a reasonably strong hash function. And now an attacker has gained access to our user table. My assumptions are:

  • To an attacker trying to obtain the password of a specific user, none of the above techniques will give a significant advantage compared to the others; all passwords are hashed by the same algorithm and with a random salt.

  • In the case, the attacker will be trying to obtain passwords for all the users the first technique will give him the advantage that all the passwords share the same salt and hence reduce the computational complexity of the entire task. But from my point of view, the second and the third techniques make no substantial difference even in this case.

So what is the advantage of generating random salts and storing them despite the additional overhead of the system?

While asking this question I was prompted to this question: Use of salt to hash a password which partially confirms my assumptions. Please do not take it personally if I don't confirm an answer immediately, I would like to see if there are different opinions out there, before confirming.

kelalaka
  • 49,797
  • 12
  • 123
  • 211
Newbie
  • 103
  • 2

2 Answers2

2

In short, a unique salt killed the Rainbow tables. In addition, you need to force your users to construct good passwords like diceware and a good password hashing algorithm with nice parameters.

So what is the advantage of generating random salts and storing them despite the additional overhead of the system?

The hash table, actually a rainbow table much suits here due to the size of the password search space, is needed to construct for every unique salt. If you use a global salt then the dedicated attacker only needs to build one table instead of many.

Interestingly, if you are going to use a unique nonce for every user, why do attackers need to build a table? They will just the brute force search since the rainbow table has no other uses. That is why rainbow tables are dead.

Of course, either they built a hash table or not, there is a limit of what they can search about. For example, the Bitcoin Miners reached $\approx 2^{92}$ double SHA256 hashes per year. Therefore if you force your users to choose high entropy passwords you will be safe.

To be safe, follow the common guides;

  1. Unique salt for each user

  2. You need to teach your users to select good passwords with high entropy like diceware or Bip-39.

  3. Password cracking is highly related to the speed and required size of the password hashing algorithm. You can choose PBKDF2, Scrypt, or the new one, the winner of the password hashing competition Argon2 to hash your passwords. Argon2id is designed to use large memory to reduce the GPU type attacks by using large memory. You need to design your system so that the password hashing took around 1 second per user. This can be like 1 million iterations for PBKDF2. In this case, you slowed the attacker 1 million times.

  4. Use also a pepper that is not stored in the database but in the application servers that increases the attacker's jobs. It can be helpful in some cases. Especially the common attacks are on databases like injection attacks.

#2 vs #3

hash( global_salt + username + password ) vs hash( random_salt + password )

#3 and #2. seems secure since the username will be unique for each user, this will make the global salt a random salt for each user. But still non-standard and not tested. Forcing a high entropy password and using a good password hashing mechanism is necessary. Attacks always get clever. Maybe the attacker can consider the username as a part of the password and try to attack it in this way. This may require calculations on the actual numbers.

kelalaka
  • 49,797
  • 12
  • 123
  • 211
0

Your #1 is insecure as two users with the same password on a system will have the same hash.

Your #2 and #3 appear to be secure, but non-standard.

There are two basic ways to do this:

  1. Hash based on a per instance salt and the user specified password.

  2. Hash based on a per instance salt, a per system salt (sometimes called a pepper) and the user specified password

The pepper is stored in a different location from the salt so if an attacker gets just the hash values and salts he still can't start easily brute forcing the password without the pepper too.

I suggest you read through NIST's Digital Identity Guidelines (p. 25) which discuss this, including proper salt lengths, etc.

Swashbuckler
  • 2,126
  • 11
  • 8