Password Hash Upgrade
Field: Security · Also called: legacy hash migration, password hash migration, rehash on login, hash layering, hash wrapping, password upgrading
A password hash upgrade is the process of moving stored passwords from a weaker hashing scheme to a stronger one. It is difficult because the verifier never has the passwords themselves, only their hashes, and sees each plaintext password only briefly when that user logs in.
The challenge
A system storing passwords with a weak scheme, such as unsalted MD5 or SHA-1, cannot simply recompute every record with a modern password hashing function, because doing so requires each original password. The verifier only sees a password when its user logs in, and some users never log in again. Any record left on the old scheme keeps the weakness of that scheme for as long as it exists.
Common approaches
- Rehash on login: when a user logs in successfully with a password stored under the old scheme, the verifier rehashes it with the new scheme. Django does this automatically. On its own, this leaves every inactive account on the old scheme indefinitely; Django’s documentation notes that the upgrade “may never happen if a user doesn’t return to your site.”
- Layering, or wrapping: the existing hashes are used as the input to the new scheme, for example
bcrypt(md5(password)), so every record can be upgraded immediately without knowing any password. The verifier then checks each record according to its scheme. OWASP’s Password Storage Cheat Sheet calls this “layering the hashes” and recommends replacing each layered hash with a direct hash of the password at that user’s next login. Django calls the same technique a “wrapped” password hasher. - Expire and reset: OWASP also describes expiring and deleting the old hashes of users who have been inactive for an extended period and requiring them to reset their passwords.
These are often combined. Layering removes the weak records at once, and rehash on login then retires the layered records over time.
Risks
OWASP notes that layering “avoids the need to know the original password; however, it can make the hashes easier to crack,” and describes password shucking, in which an attacker who holds leaked hashes from the old scheme tests them directly against the layered hashes. Django notes that a database holding hashes in more than one scheme can allow user enumeration through differences in login timing.
History
In 2015, the password-cracking group CynoSure Prime recovered more than 11 million passwords from the Ashley Madison breach, even though the site stored passwords as bcrypt hashes. Alongside each bcrypt hash, the site kept an automatic login token, $loginkey, computed as an MD5 hash that included the lowercased username and password. A code change on June 14, 2012 stopped deriving new tokens from the plaintext password, but tokens were regenerated only when users changed their account details, so tokens made the old way remained in the database and could be cracked far faster than the bcrypt hashes.
Sources
- Password Storage Cheat Sheet (Upgrading Legacy Hashes), OWASP Cheat Sheet Series. Retrieved October 2, 2026.
- Password management in Django (Password upgrading), Django documentation. Retrieved October 2, 2026.
- How we cracked millions of Ashley Madison bcrypt hashes efficiently, CynoSure Prime, September 10, 2015. Retrieved October 2, 2026.
See also
Go deeper
Articles
- What is the difference between bcrypt and SHA256? February 5, 2016