Tell me more ×
Cryptography Stack Exchange is a question and answer site for software developers, mathematicians and others interested in cryptography. It's 100% free, no registration required.

Given a database where we have usernames and passwords, we want to secure users' passwords by hashing them. We should not use only username and passwords in this hash, as someone having data from other sites that uses the same hashing function can come to the same results and thus know our passwords. If we try hashing the passwords by themselves it would be easy to bruteforce the entire database and guess all the passwords. In normal applications, passwords are hashed with a random salt that is stored along with them to prevent them from being bruteforced easily.

However, we could just use username, password and the same salt for every record in the database. This option would save one data field for each record, while the combination of username and password would be unique for our entire database. Is such a solution just as safe as the traditional password hashing scheme, or are there some security drawbacks to them I am not aware of?

share|improve this question
1  
Look, here's the thing. Say your salts are 256 bits long, which is an entirely reasonable size. That's 30 megabytes per million users. How is this an issue? Just use bcrypt and move on to dealing with problems that are actually worth your time solving. Saving a few tens of megabytes is not one of them. – Stephen Touset Jan 25 at 22:05
This answer to a bit different question on Security StackExchange is very good: security.stackexchange.com/questions/11221/… – dchest Jan 26 at 1:29
@StephenTouset I am not implementing such a system myself, I'm just curious about the problem. This question is just to satisfy that curiosity. – ThePiachu Jan 26 at 7:56
1  
The unique salt per password is there to prevent an attacker from amortizing his effort over many passwords. If you use the same salt for n passwords you make his job easier by a factor of n. Note that the salt is not secret. – starblue Jan 26 at 9:50
2  
@starblue Yes, but a single salt plus unique username would prevent the attacker from amortising their effort as they would not be able to look for more than one user's password at a time. – ThePiachu Jan 26 at 10:43

2 Answers

up vote 3 down vote accepted

Let's try to avoid random per-password salts. If the only requirement for salt is to be unique, which is the case for good password hashing schemes, you'll need:

  • $globalSalt$ is a secret random 32-byte string.
  • $userId_n$ is a unique user identifier.

You can use, for example, $HMAC$-$SHA256(globalSalt, userId_i)$ to generate salt for each user $i$. Or, with PBKDF2 or scrypt, you can just concatenate the values of $globalSalt$ and $userId_i$.

This scheme achieves uniqueness for salt only when you don't allow users to change their passwords. If you do, salt will be unique per user, not per password, so if someone changes their password, their salt will stay the same. In which case, if there are database leaks at, say, three points in time, given that the salt is unique per user, not per password, the attacker gets some information about passwords (e.g. if the user changed their password to the same value.)

To make salt unique per password, you also have to include some counter of password changes, which you have to update and store, and concatenate with $userId$. Storing this counter per-user defeats the original intention, so you can have a single global counter, making sure it never repeats, in which case you can also drop $userId$ and just use this counter along with $globalSalt$.

If $globalSalt$ is leaked, the only advantage the attacker gets is that he will be able to precompute future hashes for current and future $userId$s, which doesn't seem very useful.

We avoided generating and storing random salts by building a more complex and less secure system. Thus, it's much simpler and more secure, as others said, to use random salts instead of bothering with implementing your own scheme.

share|improve this answer
2  
You said it yourself: the only requirement for a salt is to be unique. This approach does not preserve this property in a system where users can change passwords. A user who changes his password will still reuse the same salt, and the requirement of uniqueness is violated. – Stephen Touset Jan 26 at 1:13
1  
I considered the case of changing passwords later in the answer. We get system which leaks some information about passwords if there are multiple leaks of database. Is it bad? Yes. Is it better than a single per-database salt? Yes. Is it worse than random salt? Of course. – dchest Jan 26 at 1:20
Again, the question was: are there drawbacks in the system where we don't use /dev/urandom to generate salts? I listed them. I'll try to rephrase my answer. – dchest Jan 26 at 1:38
1  
That's fine. It seemed like your original answer was encouraging the practice of a static per-user salt. Your edits are an improvement. – Stephen Touset Jan 26 at 5:54

Let's play the devil's advocate for a moment, and assume the attacker can reasonably guess that a shared salt is used, and the attacker can create an account on the system:

  1. The attacker creates an account, and supplies a known-by-him password for this account.
  2. The attacker finds his own password hash in the stolen data. He concentrates on brute-forcing this hash. He already knows the password, and focuses on the salt and composition. Assuming the hash isn't too CPU intensive, he will probably succeed with some patience.
  3. After successfully brute-forcing his own password hash, the attacker knows the salt for the entire dataset, and can easily attack the rest of the password hashes.

Additionally, if two different users have the same password, then your 'static' salt does not obscure this fact (the hashes would be identical). This could help an attacker to find higher-value hashes to break.

( For edutainment, Ars Technica has a good and quickly read article on how modern password hash attackers operate. )

So a reasonable password storage scheme would never use a static 'salt' for the entire system; it would have a random salt for each password. A random salt per password is known to work, is easy to generate, and uses only negligible space in the database. See the 'salt' tag for more info.

But even more importantly, please don't write your own code for password hashing. Use a established, well tested library which uses a well established computationally expensive hash. If you happen to develop in Ruby, then bcrypt-ruby or Devise for Rails are reasonable choices. For Python, there is Passlib. Whatever your programming language of choice might be, there is probably a well tested, mature, reviewed password hashing library available.

share|improve this answer
Assume for a moment that we use a PRF with a single 32-byte randomly generated secret key in counter mode as PRNG for salt: PRFk(0) for the first user, PRFk(1) for the second user, etc. Take HMAC-SHA-256 as PRF. Rename secret key to "global salt". Replace counter with unique user id. Take scrypt as password stretching function. Is scrypt(password, HMAC-SHA-256(globalSalt, userId)) a sound password hashing scheme? – dchest Jan 25 at 23:24
1  
In 2 you have a pretty big assumption. It's infeasible to bruteforce a long enough "shared salt" if it's random and the hash function is good. – dchest Jan 26 at 0:47
I literally cannot stress this enough: why jump through all those hoops? Generating a cryptographically random value of appropriate size is not difficult. Storing this value is not difficult. What on earth is the purpose of jumping through all these hoops, when the approach recommended by security professionals is so unbelievably trivial? – Stephen Touset Jan 26 at 0:56
2  
@StephenTouset I'm not suggesting that it's a usable scheme. We're on Cryptography StackExchange, where we discuss a lot of theoretical stuff; otherwise we might as well replace the whole site with the page that says "use NaCl for encryption and scrypt for key derivation" ;) – dchest Jan 26 at 1:01
1  
@JesperMortensen bruteforcing attacks against sufficiently large random strings (e.g. 16-32 byte) are infeasible. They apply to passwords because they don't have enough entropy. Your attack fails at step 2 if you don't know the "secret global salt" (which should be properly called "a key") if it has e.g. 128 bits of entropy. Of course, stealing this key is probably just as likely as stealing database. I'm just pointing out that your attack as described doesn't work. – dchest Jan 26 at 21:46
show 2 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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