Search code examples
phpalgorithmhashbcryptsha1

Issue Migrating Hashing Algorithm in PHP


I am looking at migrating the hashing algorithm used for all passwords in a database and looking for proof of concept.

I was reading up on a concept that would be very user friendly and not force anyone to change their password here that essentially allows to migrate all passwords in the database at once by hashing the hash:

The idea is basically to hash the hash. Rather than waiting for users provide their existing password (p) upon next login, you immediately use the new hashing algorithm (H2) on the existing hash already produced by the old algorithm (H1):

hash = H2(hash) # where hash was previously equal to H1(p)

After doing that conversion, you are still perfectly able to perform password verification; you just need to compute H2(H1(p')) instead of the previous H1(p') whenever user tries to log in with password p'.

So essentially I would:

  1. Using the new algorithm, re-hash all of the passwords currently stored in the database with the old hash algorithm.
  2. When users try to authenticate, wrap the old algorithm with the new one to verify the password.

Let's say I am migrating from SHA1 to BCRYPT, I run the following test with a password of 11111111:

// 1. Old hash algorithm used to store the password in the database and authenticate the login
hash('SHA1', '11111111') 
// result: a642a77abd7d4f51bf9226ceaf891fcbb5b299b8

// 2. This would be run to update the hashed values in the database to the new algorithm
password_hash('a642a77abd7d4f51bf9226ceaf891fcbb5b299b8', PASSWORD_BCRYPT)
//result: $2y$10$zetRoEVYvjug73Ee8k/cSOSLxFBLs0fNYGJDrdqFkyqGoe41baJ46

// 3. This would be run to authenticate a user after the database passwords have been updated to the new algorithm
password_hash(hash('SHA1', '11111111'), PASSWORD_BCRYPT)
// result: $2y$10$xANTOzHQaKxfKKqzTn9lVeJIgHH3YQok/eOegIeRmrHHUTJkx7pDS

Theoretically the result of 2. and 3. above should be the same, and would need to be for this to work - but they are not.

Is there something I am missing?


Solution

  • Theoretically the result of 2. and 3. above should be the same, and would need to be for this to work - but they are not.

    No, they shouldn't, and they don't have to be. BCrypt generates its own cryptographic salt which is unique and random, producing a unique random hash for same input.

    You don't need (or want!) the same hash produced each time. You can still compare the candidate password (passed through SHA*) against the stored bcrypt hash.

    You do not run password_hash(hash('SHA1', '11111111'), PASSWORD_BCRYPT) to test a candidate password against a stored bcrypt hash. You use password_verify which takes care of salting the input and performing the comparison.