Skip to content

Instantly share code, notes, and snippets.

@ircmaxell
Created June 21, 2013 16:29
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ircmaxell/5832433 to your computer and use it in GitHub Desktop.
Save ircmaxell/5832433 to your computer and use it in GitHub Desktop.
password-migrate proof-of-concept
<?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);
}
<?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