9

I'm pretty new to the whole password hashing business, so I might be missing something obvious.

I was looking at the bcrypt algorithm, in particular BCrypt.Net, and I was wondering if it wouldn't be more secure to compute a unique salt for each user instead of a random salt ?

At the moment the salt and the workload (# rounds) are exposed in the hashed password string. ($2a$ + 2 digit workload + $ + 22 character salt + 31 character hashed password) Aren't we helping potential hackers by giving them the salt (if they, for example, want to brute force the admin's password) ?

Couldn't we calculate the salt for each user by hashing (for example with MD5 or SHA1) their email address ?

If we then only save the 31 last characters of the bcrypt hashed password, leaving out the identifier, the workload (if we keep it fixed) and most importantly the salt. Wouldn't this make it a lot harder for person trying to find the password ?

(And on the plus side, we would gain some database space for large user databases, as the password is only 31 characters instead of 60)

I'm probably missing something obvious, as I can't be the first person to make this reflection.

Marc

Marc
  • 193
  • 1
  • 2
  • 8

3 Answers3

9

You do not want to use a fixed salt for a given user because it could lead to salt reuse, which is bad (that's the capital sin of salts, since the whole idea of salts is not to be reused; they have no other function). Salt reuse can occur, practically, for the two following reasons:

  • If a user changes his password, the new password will use the same salt than the old. That's bad, because old passwords are still valuable (since users reuse passwords, the old password can be the password that the user will set next month; it can also be the password for the same user on another site), and the reuse will allow the attacker to attack both the old and new password for the cost of one brute force.

  • If the salt is derived from the user name, then another instance of the server software, on a distinct server, may end up with the same salts, because "Administrator" tends to always be called "Administrator". Attackers could precompute a big table (e.g. rainbow table) for "Administrator", applicable to many sites (which would make table building worth the effort).

I usually sum this up by stating that salts must be unique timewise (no reuse of an old salt value) and worldwide (no reuse of salts anywhere, even on a distinct server). Such uniqueness is hard to get, but there is nonetheless a rather simple way, which is to generate big enough salts (e.g. 128-bit salts, as bcrypt uses) at random.

Summary: not generating bcrypt salts randomly would not be more secure; at best, this would be equally secure, but, more probably, less secure.

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
  • Thanks, that answer actually made sense :). The fact that the algorithm used to calculate the salts can not be changed once it's in use is indeed a problem. If the hacker, one way or the another, obtained the algorithm used to compute the salts, then even changing the passwords won't change the salt for a user. I'll mark this as the best answer. – Marc Sep 25 '12 at 18:12
3

The reason for a salt is, to prevent attacks with pre calculated rainbow tables. The attacker would have to build one rainbow table for each password, and that doesn't make sense (it's easier to brute-force until you found a match). So the job of the salt is done, even when it is visible plaintext in the hash-value.

If you want to add a secret to the calculation, then you can add an additional pepper. In contrast to the salt, the pepper will not be stored in the database. This will help against dictionary attacks, as long as the attacker has only access to your data (e.g. SQL-Injection) but not to your code.

I would not mix up salt and pepper, because they serve a different purpose. And to answer your question, use a random salt, and add a pepper if you want to add something hidden.

martinstoeckli
  • 5,149
  • 2
  • 27
  • 32
1

The salt in bcrypt is 128-bit long and randomly generated, so you'd need 2^127 users before there was a 50% chance of a collision. Let's put that in perspective: every person on earth could make a user account for every atom in their body, and we'd still only be about 1/3rd of the way there.

Use the random salt. Solutions involving computed salts introduce some subtle issues that can ultimately lead to the difference between a hassle and a catastrophe.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • Actually the salt is 128-bit long (otherwise you would need "only" four billion salts to risk a collision). – Thomas Pornin Sep 25 '12 at 15:42
  • @ThomasPornin My bad - misread the spec. Editing now. – Polynomial Sep 25 '12 at 15:43
  • @Polynomial: nice article you linked and wrote, but most of it I already knew. But I don't see what could be the issue with computed salts. Of course, in an ideal world we would detect when a hacker gains access to our db, but what if a hacker gains undetected access, dumps our user table and quietly leaves. He now has access to the plaintext salts and can brute force the passwords. In this case, isn't it safer not to store the salts ? And I don't understand your remark about collisions. If the salt is hidden, the hacker won't even know if there was a collision between 2 computed salts. – Marc Sep 25 '12 at 16:17
  • @Marc [Kerckhoffs' principle](http://en.wikipedia.org/wiki/Kerckhoffs%27s_principle) - you have to assume that the attacker *knows* the algorithm for generating the salts, but have your system remain secure despite this. – Polynomial Sep 26 '12 at 07:37