PHP 7: Maintaining and Upgrading Legacy Apps · Security Hardening for PHP 7 Apps
Upgrading Weak Password Hashes
Move from md5/sha1 hashes to password_hash without forcing resets.
Old apps store md5($password) or sha1($salt . $password). You can't reverse them, but you can migrate on login: verify with the legacy algorithm, then rehash with password_hash() and store it. For inactive users, wrap the old hash immediately: store password_hash(md5_hash) so weak hashes never sit in the database, and verify with password_verify(md5($input), $stored).
<?php
function verifyAndUpgrade(PDO $pdo, array $user, string $password): bool
{
$hash = $user['password_hash'];
if (strpos($hash, 'legacy-md5:') === 0) { // wrapped legacy hash
$ok = password_verify(md5($password), substr($hash, 11));
} else {
$ok = password_verify($password, $hash);
}
if ($ok && (strpos($hash, 'legacy-md5:') === 0 || password_needs_rehash($hash, PASSWORD_DEFAULT))) {
$pdo->prepare('UPDATE users SET password_hash = ? WHERE id = ?')
->execute([password_hash($password, PASSWORD_DEFAULT), $user['id']]);
}
return $ok;
}
// One-off migration for all rows:
// UPDATE: password_hash = 'legacy-md5:' . password_hash(old_md5, PASSWORD_DEFAULT)
Common pitfalls
- Leaving raw md5 hashes in the database until users log in.
- Comparing hashes with
==(type juggling with '0e...' strings). - Using PHP 8-only helpers like
str_starts_with()in code that must still run on PHP 7.
Key points
- Wrap weak hashes immediately
- Upgrade to password_hash on login
- Never compare hashes with ==
Further reading: OWASP Password Storage Cheat Sheet
Practice questions
How can you protect md5 password hashes for users who never log in again?
Answer
Wrap them: replace each md5 hash with password_hash(md5_hash) in a migration, then verify with password_verify(md5(input), stored) and rehash properly on next login.
Why is md5($a) == md5($b) dangerous in PHP?
Answer
Loose comparison treats strings like '0e123...' as numbers in scientific notation (0), so different 'magic hashes' compare equal. Use hash_equals() or password_verify().
Open in the app to mark lessons complete, take notes and track your progress.