15

I read this blogpost on how to store passwords in a database, the secure way. Elbert tells us to do this:

  1. Generate a random salt and include something random and the username in it. Hash this.
  2. Hash <hashed-salt><password> a lot times.
  3. Store <hashed-salt><hash> in the database.
  4. Regenerate the salt every time a user logs in.

Now, for checking if a password is okay:

  1. Find the hashed salt in the database.
  2. Hash <found-salt><password> a lot times.
  3. Check if the value is the same as the hash you can find in the database.

I understand why this works. I do not understand why this is better than just hashing / salting a password. With this solution, the hashed-salt is in plaintext in the database, so when someone has access to the database, he has access to the hashed salt. Assuming he has access to the scripts as well, he can again perform a brute force attack. This brute force attack won't be fast, because we hashed a lot times, but still, that does not explain what the random salt adds in security relatively to salting with a constant word.

Why does using a random salt which can be found in the database add security over using a constant salt which can be found in the scripts?

  • I believe the wikipedia article you are looking for is on [rainbow tables](http://en.wikipedia.org/wiki/Rainbow_table) – Bill Lynch Mar 31 '13 at 15:15

4 Answers4

15

"Why does using a random salt which can be found in the database add security over using a constant salt which can be found in the scripts?"

Because the "constant salt" (which would really be better called "pepper") is the same for all the password hashes, and therefore does not satisfy the primary purpose of a salt — ensuring that, even if two users have the same password, their hashes will be different.

In particular, let's say you have 1000 users, and that your user database with the password hashes gets stolen by an attacker. Very likely, the attacker can also steal your scripts, and thus also the pepper included in them. (This may not always be the case — for example, SQL injection attacks might compromise the database without necessarily compromising the application code — but you should not assume that it can't happen.)

To crack the password hashes, the attacker can then just guess random passwords by brute force (or, for a first pass, run through a list of common passwords), hash each of them with your fixed pepper and look the resulting hash up in a database of the stolen hashes. Notably, they need to do this only once for each guessed password, just as if you hadn't used the pepper at all.

However, if you had instead used a unique salt, stored in the database, for each password hash (with or without the pepper as well), then hashing a guessed password with one of the salts and comparing the result with the stolen hash just tells the attacker whether that particular user was using that particular password. Thus, the time needed to crack all the passwords (or to crack just any one or any n of them) is increased by a factor of 1000 (the number of users in your database).

Or, to look at it another way, not using a unique salt for each password hash in a 1000-user database makes the attacker's job 1000 times easier. Even if you used, say, PBKDF2 with an iteration count of 1000 to slow down brute force attacks, well, guess what — not using unique salts just ate away any benefit you got from using PBKDF2. Oopsie.


Now for the other part of your question, the article you linked to suggests a rather complicated method for choosing the salt:

$salt = hash('sha256', uniqid(mt_rand(), true) . 'something random' .
             strtolower($username));

Why so complicated, when the only property the salt really needs to satisfy is uniqueness?

Well, the salt really should not just be unique within your database, but globally unique, so that even an attacker with multiple stolen databases won't find cracking them any easier. In some attack scenarios, it's also desirable for the salts to be unpredictable, so that an attacker can't start precomputing a password table before they've stolen your database, hopefully giving you more time to respond if you detect the breach.

A relatively simple way to achieve both of these goals is to make the salts sufficiently long and random (where sufficiently long means, say, at least 100 bits of entropy). However, generating truly random salts can be kind of tricky, especially in PHP which, unfortunately, still lacks a standard portable method for obtaining cryptographically secure random numbers.

Thus, the example code you linked to appears to be going for a robust "belt and suspenders" approach: by concatenating several pieces of data of varying randomness and uniqueness and feeding them to a cryptographic hash function, we can be fairly sure that the output will be as unique and random as the sum of the inputs (up to limits imposed by the hash output length, anyway, which for a 256-bit hash like SHA-256 are basically infinite). In particular, as long as at least one of the inputs is unique (resp. random), we can be essentially sure that the output will also be so.

As a general principle, this is a perfectly good and commendable approach to security. One might quibble about the specific choice of inputs suggested in the article (and, in particular, about the lack of even any attempt to obtain true cryptographic randomness from sources like openssl_random_pseudo_bytes() or /dev/urandom on Unix systems), but at least the general design is sound.

In particular, note that the suggested inputs to the hash function include the following:

  • the username (although why it's lowercased, I have no idea), ensuring that all users on the system will have unique salts,
  • a hopefully unique per-system identifier (the "something random"), which should ensure that the same user will have different salts on different systems,
  • the current time (obtained via uniqid()), which ensures that a user will get a different salt every time they change their password, and
  • various other bits of (pseudo)randomness obtained via mt_rand() and uniqid(), which may introduce some unpredictability (and, at least in some hardened versions of PHP, possibly even some real randomness), and at least won't do any harm.
Ilmari Karonen
  • 4,406
  • 20
  • 28
9

There is no point to the scheme you describe. The answer provided by Matrix isn't entirely correct as well.

The only requirement of a salt is uniqueness. The easiest way to achieve this is through the use of a CSPRNG. However, being random is not a required property of a salt.

However, do avoid the use of usernames or emails as a salt. This is to prevent attackers from generating rainbow tables with admin or similar as a salt to perform a targeted attack against a particular user.

  • 3
    Indeed, the salt does not need to be random, just unique. However, I'd say random salts are better as it makes for a simpler system because you don't have to worry about uniqueness. – Matrix Mar 31 '13 at 16:02
  • @Matrix Agreed, like I mentioned, the easiest way is through the use of a CSPRNG. I just disagree with your point about `mt_rand` not being a CSPRNG being relevant to salting. –  Mar 31 '13 at 16:04
  • While what you say is true, note that salts should be _globally_ unique across all systems in the world, not just within your own system, which is hard to do except by making them random. – Mike Scott Jan 18 '16 at 21:23
3

Because of rainbow table (https://en.wikipedia.org/wiki/Rainbow_table). With an identical salt for all the users, the attacker simply needs to create a rainbow table with that salt and X number of hash iterations. If each user has a unique salt, creating rainbow tables is no longer practical and it's faster to do a brute-force search.

I've read the article you've linked and I don't recommend you use the code from it because it suffers from several flaws.

  1. mt_rand() is not a cryptographically secure source of randomness.

  2. uniqid() is not a cryptographically secure source of randomness.

  3. Adding username to a random salt makes no sense.

  4. 'something random' appears to be a pepper without being marked as so. Think of pepper as a site-wide salt. It only provides marginal security benefits.

  5. Making your own password hashing solution is not a good idea. It's better to use either http://www.php.net/manual/en/function.hash-pbkdf2.php or http://www.php.net/manual/en/function.crypt.php (with bcrypt - Blowfish). Also: Why shouldn't we roll our own?

  6. Dynamically generating and executing SQL statements directly in application is not a good idea because of risk of SQL injection in case the input is not properly escaped. Prepared statements are better and stored procedures even better.

Matrix
  • 4,028
  • 14
  • 25
0

The point of salt is to make brute force attacks and interpretation of the hashed password harder:

If you are looking on a single password then there's not difference. If however you have 1000 users then with an unsalted password entry you can perform a dictionary attack by hasing each entry once and comparing against the hashed entries. By using a salt you will have to do N hashes of each word, where N is the number of unique salts between the 1000 hashes (ideally that will be 1000, but you may have salt collisions). A salt also makes it impossible to determine which users are using the same password.

The above means that you can have a couple of thousand users with just a couple of bytes for salt and you can just increment the salt by one.

This however is insecure in a larger scope. Since you will be using a well know encryption method, one that gets your hashed database can use a precompiled list of hashed passwords or a rainbow table or just combine yours with another one's. To prevent that you need (long) hashes. This means that hashed passwords become more secure if you can ensure that they are fairly unique among other hashes on earth.

V13
  • 101
  • 1