1

I am checking out the bcrypt hash-algorithm.

My first test with password_hash():

echo password_hash("123", PASSWORD_BCRYPT, array( "salt" => "1234567890123456789012" ));
echo password_hash("123", PASSWORD_BCRYPT, array( "salt" => "1234567890123456789012xxxxxxxxxxxxxxx" ));

Both will return '$2y$10$123456789012345678901uiaLpJxTpf6VbfI5NADlsRsfvEm6aq9C'.

  1. Why the heck is the salt stored inside of the hash? This makes no sense at all for me. A attacker who gets the hashes from a database can´t do anything with them, if he does not know the salt.
  2. Why do I get the same Hash with two different salts? Are only the first 22 chars used for the salt passed to the function?

Thank you very much!

Tream
  • 1,049
  • 2
  • 13
  • 29

2 Answers2

3

The salt isn't a secret, it's generally stored in the database with the hash, and could just as well be stored directly in the hash, like password_hash does.

The salt creates uniqueness so the hash can't easily be cracked with things like rainbow tables or dictionaries, it doesn't really add security other than making the hash more unique so running a dictionary or table against the hash doesn't match because it also includes the salt.

If you omit the salt, a random salt will be generated by password_hash() for each password hashed. This is the intended mode of operation, and you shouldn't supply your own salts.
PHP7 will actually produce a warning telling you that using the salt option is deprecated.

The salt passed needs to be at least 22 characters, but most underlying algorithms, like bcrypt, doesn't use the entire salt, see this answer for more on that

Community
  • 1
  • 1
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • Thank you - and the others - for your replies! I do understand how a salt works. Currently I am using SHA256, and as Salt I use a very long Salt (hardcoded in a config PHP file) + the UserID, so, when two users have the same password, they will have a different password hash. I still think that making the salt "secret", the security is incresed by a lot. If the user have a 8 chars password I dont need a rainbowtable, I can just bruteforce it very fast. But if I dont know the salt, I can´t do anything. – Tream Jun 07 '15 at 01:17
  • Of course bruteforcing bcrypt is another story... I just though of that. – Tream Jun 07 '15 at 01:19
  • 2
    Well, that's up to you, if you think keeping the salt a secret increases your security by a lot, then keep it secret, it sure won't hurt, but we're basically telling you it won't matter either. If someone gets access to the stored hashes, they probably have access to the user id's and the stored salt in the config anyway, and that's why the salt should be a unique string for each hash, it makes it a lot more difficult to just run the entire list of hashes against a dictionary when you have to add the unique salt for each hash. – adeneo Jun 07 '15 at 01:25
  • "Currently I am using SHA256" Bad. "as Salt I use a very long Salt (hardcoded in a config PHP file)" Bad. The docs explicitly warn against doing this. "If the user have a 8 chars password I dont need a rainbowtable, I can just bruteforce it very fast" Not with bcrypt, you can't. You're supposed to tune bcrypt so it takes ~0.1 second to hash a password. 8 character passwords have 3,025,989,069,143,040 possible combinations, which means brute forcing it takes **thousands of millennia**. – ceejayoz Jun 07 '15 at 02:19
3

Salts are not something you have to strive to keep secret. Their protection is effective even when known. https://crackstation.net/hashing-security.htm

The salt does not need to be secret. Just by randomizing the hashes, lookup tables, reverse lookup tables, and rainbow tables become ineffective. An attacker won't know in advance what the salt will be, so they can't pre-compute a lookup table or rainbow table. If each user's password is hashed with a different salt, the reverse lookup table attack won't work either.

Since you appear to be using a fixed salt value, heed this:

A common mistake is to use the same salt in each hash. Either the salt is hard-coded into the program, or is generated randomly once. This is ineffective because if two users have the same password, they'll still have the same hash. An attacker can still use a reverse lookup table attack to run a dictionary attack on every hash at the same time. They just have to apply the salt to each password guess before they hash it. If the salt is hard-coded into a popular product, lookup tables and rainbow tables can be built for that salt, to make it easier to crack hashes generated by the product.

I'd suggest using password_hash without its optional parameters. The default function is built to be highly secure and by specifying your own algorithm and options you potentially weaken its function.

Per the PHP documentation:

Caution It is strongly recommended that you do not generate your own salt for this function. It will create a secure salt automatically for you if you do not specify one.

edit: Here's why keeping the salt secret in bcrypt is pointless.

Per this post, there are 3,025,989,069,143,040 possible combinations in an 8 character password. You should generally tune bcrypt's work factor to take 0.1 seconds to hash a password. This means calculating all possibilities takes 302,598,906,914,304 seconds. That is 9,588 millennia.

Community
  • 1
  • 1
ceejayoz
  • 176,543
  • 40
  • 303
  • 368