I was reading up on password security in PHP and I stumbled upon an interesting statement:
Hashing the password with its hash as the salt returns the same hash
Without much thinking, I went on php.net and found that is says the same thing.
Let's look at an example:
crypt("test", "test"); -> teH0wLIpW0gyQ
crypt("test", "teH0wLIpW0gyQ"); -> teH0wLIpW0gyQ
I can totally understand that crypt in PHP generates a one-way hash of the given string.
Follow up
Thank you all for your pointers. I can see now that the default behavior is to use only the first two characters of the salt, which totally answers all my questions. Feels like a silly thing, but...
A cryptographic salt is made up of random bits added to each password instance before its hashing. Salts create unique passwords even in the instance of two users choosing the same passwords. Salts help us mitigate hash table attacks by forcing attackers to re-compute them using the salts for each user.
Password hash salting is when random data – a salt – is used as an additional input to a hash function that hashes a password. The goal of salting is to defend against dictionary attacks or attacks against hashed passwords using a rainbow table.
Assuming the salt is very long, not knowing the salt would make it nearly impossible to decrypt hash password with salt(due to the additional length that the salt adds to the password), but you still have to brute force even if you do know the salt.
Password hashing makes storage and management more secure, and applies to both salted and unsalted passwords. Salted passwords that are also hashed make it harder for bad actors to crack passwords at scale.
This is done on purpose. Your crypt function, when second argument consists of letters and digits, uses only two first characters of "salt" for encrypting, and those two characters are placed at the beginning of the result. So,
crypt("test", "test"); -> teH0wLIpW0gyQ
crypt("test", "te"); -> teH0wLIpW0gyQ
crypt("test", "tea"); -> teH0wLIpW0gyQ
crypt("test", "temperature"); -> teH0wLIpW0gyQ
etc.
This is done for easy password correctness checking, so thatcrypt($password, crypt($password, $salt)) == crypt($password, $salt)
1. What I don't understand is how can we obtain the same hash output using two completely different salts?
Although you provided different salts to the crypt
function, it uses the same salt internally, i. e., te
. This is due to how crypt
is implemented:
Standard DES-based hash with a two character salt from the alphabet "./0-9A-Za-z".
So even if you provide a salt longer than 2 characters it will only take the first two.
And as crypt
’s output contains the used salt prepended to the calculated hash, using a crypt
hash as salt results in exactly the same output. And that’s just perfect as then the following can be used to verify a stored password:
crypt($password, $hash) === $hash
2. Does it mean there are possibly other salts that could possibly give me the same hash?
Yes. This does also apply to other crypt
algorithms like bcrypt.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With