Created
June 21, 2013 16:29
-
-
Save ircmaxell/5832433 to your computer and use it in GitHub Desktop.
password-migrate proof-of-concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
function runMigration() { | |
foreach (getUsers() as $user) { | |
$hash = $user->hash; | |
list ($oldhash, $oldsalt) = explode(':', $hash, 2); | |
$newHash = password_migrate_create($oldhash, $oldsalt, PASSWORD_BCRYPT); | |
$user->hash = $newHash; | |
saveUser($user); | |
} | |
} | |
function login($username, $password) { | |
$user = fetchUser($username); | |
$legacyAlgo = function($password, $hash, $oldsalt) { | |
return md5($password . $oldsalt); | |
}; | |
$test = password_migrate_verify($password, $user->hash, $legacyAlgo, PASSWORD_BCRYPT); | |
if ($test) { | |
if (is_string($test)) { | |
$user->hash = $test; | |
saveUser($user); | |
} | |
// Login is successful | |
} else { | |
// Incorrect password | |
} | |
} | |
function register($username, $password) { | |
$user = createUser($username); | |
$user->hash = password_hash($password, PASSWORD_BCRYPT; | |
saveUser($user); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Verify old and new hashes | |
* | |
* @param string $password The password to verify | |
* @param string $hash The hash to verify against | |
* @param callable $legacyAlgo A callback expecting ($password, $hash) to test old legacy hashes | |
* @param int $newAlgo The new aglorithm to use with password_hash | |
* @param array $newOptions The new options to use with password_hash | |
* | |
* @return bool|string A boolean result if not valid, true if valid and new algo. Returns an updated hash if needed. | |
*/ | |
function password_migrate_verify($password, $hash, $legacyAlgo, $newAlgo, array $newOptions = array()) { | |
$needsRehash = false; | |
$origPassword = $password; | |
if (substr($hash, 0, 8) === '$legacy$') { | |
$hash = substr($hash, 8); | |
list ($options, $hash) = explode('$', $hash, 2); | |
$password = $legacyAlgo($password, unserialize(base64_decode($options))); | |
$needsRehash = true; | |
} | |
if (password_verify($password, $hash)) { | |
if ($needsRehash || password_needs_rehash($hash, $newAlgo, $newOptions)) { | |
return password_hash($origPassword, $newAlgo, $newOptions); | |
} | |
return true; | |
} | |
return false; | |
} | |
/** | |
* "Wrap" legacy hashes in a new style hash (to protect them further) | |
* | |
* @param string $legacyHash The legacy hash to wrap | |
* @param mixed $legacyOptions Options that need to be preserved (passed to the legacyAlgo callback) | |
* @param int $newAlgo The new algorithm to use with password_hash | |
* @param array $newOptions The new options to use with password_hash | |
* | |
* @return string A new hashed value to store. | |
*/ | |
function password_migrate_create($legacyHash, $legacyOptions, $newAlgo, array $newOptions = array()) { | |
if (substr($legacyHash, 0, 8) === '$legacy$') { | |
throw new RuntimeException('Attempting to migrate already migrated password!'); | |
} | |
$newHash = password_hash($legacyHash, $newAlgo, $newOptions); | |
return '$legacy$' . base64_encode(serialize($legacyOptions)) . '$' . $newHash; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment