0
$\begingroup$

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.

$\endgroup$
3
  • 2
    $\begingroup$ The cost of recalculating a rainbow table may pay up when the salt is reused. $\endgroup$
    – shumy
    Feb 7, 2020 at 18:32
  • 1
    $\begingroup$ a global salt is only supposed to be used as a prefix to a user salt... otherwise it is not a salt, it is a password prefix $\endgroup$ Feb 7, 2020 at 22:21
  • 1
    $\begingroup$ also 3 is a terrible idea, as a username+password string concatenation can be identical for multiple users with similar usernames and passwords like "Steve"+"sbox" and "Steves"+"box" $\endgroup$ Feb 7, 2020 at 22:23

2 Answers 2

2
$\begingroup$

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.

$\endgroup$
8
  • $\begingroup$ I see your point, but I may have missed a shade of your answer. My question is explicitly about comparing unique salts against something like this hash( global_salt + username + password ) or even hash( global_salt + hash( username ) + password ). Which implicitly are unique hashes but never stored as they are derived on a user base from a ahead known info. This is the actual core of my question, are you answering this? $\endgroup$
    – Newbie
    Feb 7, 2020 at 18:41
  • $\begingroup$ Exactly, therefore technique #3 is comparable to #2 in terms of vulnerability? $\endgroup$
    – Newbie
    Feb 7, 2020 at 18:52
  • $\begingroup$ Why? As mentioned the question it theoretical, so is more a thought, but for example being the username the login pivot i can produce the password_hash upfront (before event extracting the user from DB and hence have a more homogeneous response time from my system in both cases; when password is wrong and user exists and when user do not exists. Hence removing a timing footprint that will allow for username search on my system. And I was just started wondering if there was a specific reason i can not find this technique around, despite being easier to implement. $\endgroup$
    – Newbie
    Feb 7, 2020 at 19:06
  • $\begingroup$ The password's hash is already stored in the database usually users_table. When user logins, you take the username and entered password then you query the DB to get the hashed value, salt and hash them with password from the user. One DB query! $\endgroup$
    – kelalaka
    Feb 7, 2020 at 19:11
  • $\begingroup$ True, I'm not disputing about the performance at all. I was just saying that when the user does not exist what do you do? You have nothing to hash, do you make a fake salt to make a fake hash to make a fake check if you want your timing to be as close as possible to the one in case user existed but password was wrong? I'm just saying that if they are comparable from a security point of view why is not used for simple authentication systems being more straight forward to implement? $\endgroup$
    – Newbie
    Feb 7, 2020 at 19:21
0
$\begingroup$

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.

$\endgroup$
1
  • $\begingroup$ The #2 is actually your point 1. (generates a per password salt that gets stored along with the password). The question is of course theoretical. And as pointed in the comment to the question above i'm explicitly asking about the difference between #2 and #3 that prevents from storing the salt, being it derived (in the third). $\endgroup$
    – Newbie
    Feb 7, 2020 at 18:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.