Regarding the PHP function password_verify()
, how is the "unhashed" random salt derived from the output of password_hash()
, given that hashing functions are deterministic? It seems to me that, if the original salt is able to be derived from the final hash, then the salt was only used to produce the final hash after the hashing function has completed its iteration count. If this is the case, why use a salt in the first place? Why not just provide some arbitrary (but still large) iteration count?
- 64,406
- 24
- 178
- 215
- 13
- 3
-
1`password_hash` and `password_verify` currently use either `bcrypt` or `argon2` which both use the [PHC string format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md). The salt is included in the hash format. It's not derived by `password_verify`, it's literally just right there in the hash you give it. – AndrolGenhald Mar 27 '18 at 14:22
-
1@AndrolGenhald You could probably post that as an answer, Androl. – Monica Apologists Get Out Mar 27 '18 at 14:38
-
@AndrolGenhald Thanks for your comment, but I'm still not understanding. I do understand that the salt is included in the output of `password_verify`, but isn't that salt hashed as well? Meaning that its original value cannot be obtained for use in verification (in a reasonable amount of time, anyway). – user1100793 Mar 27 '18 at 14:39
-
1@user1100793 Do you understand [why salts are used](https://security.stackexchange.com/q/14025/151903)? All that is required of a salt is that it be universally unique, it's not supposed to be any more secret than the hash. – AndrolGenhald Mar 27 '18 at 14:43
-
This seems more like a question about salts in general than a question about the implementation in php. Please edit your question to make clear what you are acutally interested in. Note though, that there are already a lot of questions concerning salts on this site. – Tom K. Mar 27 '18 at 14:55
-
@AndrolGenhald Perhaps my question isn't clear. I'll try again: The output of `password_hash()` is a deterministic hash value, calculated by a specified hashing algorithm being run a specified number of times on the given inputs of plaintext and random salt. Given that hash value output, doesn't `password_verify()` need the original value of the salt to "reconstruct" the output of `password_hash()` and compare this against the stored output of `password_hash()`? – user1100793 Mar 27 '18 at 14:58
-
@AndrolGenhald ... What I'm saying is... the salt value taken from the output of `password_hash()` is itself part of a hash (it is hashed), therefore obtaining its original value is exceedingly difficult, right? What am I missing? – user1100793 Mar 27 '18 at 14:58
-
@user1100793 The output of `password_hash` is a string in the PHC format that specifies the hash used, parameter(s), the salt, and the final hash. The salt is stored right there in the string before being hashed. `password_verify` obtains the salt by extracting it from the string you pass it. – AndrolGenhald Mar 27 '18 at 15:08
-
Perhaps the confusion is a result of conflicting terminology. "Hash" is often used to refer to both the result of the hashing function (the
value in the PHC format) as well as the entire PHC format string, which includes other values besides just the hash. – AndrolGenhald Mar 27 '18 at 15:09 -
@AndrolGenhald Thank you! That explains it: "The output... is a string in PHC format". And: "The salt is stored right there in the string before being hashed". And: "PHC format string... includes other values besides just the hash". In other words, the ouput is not just a hash; it is a string that contains some things in addition to the actual hash. The salt itself is not a hash value (or part of a hash value), so it can simply be extracted as used again. I'll gladly accept your answer. – user1100793 Mar 27 '18 at 15:24
2 Answers
password_hash
and password_verify
currently use either bcrypt
or argon2
which both use the PHC string format. The salt is included in the hash format. It's not derived by password_verify
, it's literally just right there in the hash you give it. This is not a weakness because a salt is not required to be secret, it must only be universally unique.
Your confusion about how password_verify
obtains the salt seems to be caused by conflicting terminology. The word "hash" is often used to refer to the entire PHC format string (including the hash function identifier, parameters, salt, and actual hash value), as well as the actual hash output by a hashing function.
See here for why salts are used. If you want a secret that an attacker would have to know to hash passwords, that's called a pepper.
- 15,436
- 5
- 45
- 50
Take a look at this extreme simplification of what goes on under the hood in PHP:
password_hash(password):
salt = get_random_salt()
hash = actual_hashing_function(salt + password)
return salt + hash
password_verify(password, password_hash):
salt, actual_hash = split(password_hash)
compare_hash = actual_hashing_function(salt + password)
return actual_hash == compare_hash
As you can see, password_hash
can not be deterministic as part of the output - the salt - depends on a random function. However, actual_hashing_function
is deterministic.
The confusion here stems from somewhat imprecise terminology. In a cryptography context, "hash function" probably referes to something like actual_hashing_function
in the example, and a "hash" means it's output. However, in a more applied context like programming, "hash function" might mean something like password_hash
and a "hash" it's non deterministic output that includes the salt.
- 64,406
- 24
- 178
- 215