Schlichte Passwort Hashing API in PHP 5.5

Tags:

PHP 5.5 wird immer wie besser. Dank dem Engagement von Anthony Ferrara und Nikita Popov wird das kommende PHP 5.5 eine einfache und einheitliche API zum Hashen von Passwörtern bieten.

Mittlerweile hat sich glaube ich ziemlich weit rumgesprochen, dass Hashes für Passwörter andere Anforderungen haben als Checksummen. Das Hauptargument ist, dass sie kosten-, bzw. zeitintensiv sein sollten. Also genau das Gegenteil von dem, was Algorithmen wie md5() und shaXXX() bieten.

Der Quasi-Standard ist momentan bcrypt. In der Zukunft wartet scrypt, welches sich aber noch etwas bewähren muss.

Bcrypt wird von PHP seit Version 5.3 zwar unterstützt, muss aber mühsam über die crypt() oder mcrypt_* Funktionen angewendet werden. Dem soll Abhilfe geschafft werden. Das entsprechende RFC wurde akzeptiert und die Implementierung steht kurz vor Abschluss.

Folgende API soll in Zukunft zur Verfügung stehen:

$pass = 'supergeheimespasswort';
$hash = password_hash($pass, PASSWORD_DEFAULT);

if (password_verify($pass, $hash)) {
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $hash = password_hash($pass, PASSWORD_DEFAULT);
        // $hash für User in DB updaten
    }

    // Yay.
} else {
    // Nay.
}

password_hash() erstellt einen Hash mit dem angebenen Algorithmus. PASSWORD_DEFAULT entspricht dabei dem vorläufig einzigen verfügbaren Algorithmus bcrypt (PASSWORD_BCRYPT). Momentan wird noch diskutiert, ob dieser Parameter optional sein soll. Dagegen spricht, dass der Entwickler bei einer optionalen Angabe vergessen könnte sich darum zu kümmern, wenn sich der Standardwert (PASSWORD_DEFAULT) ändert.

Bei dieser Problematik kommt dann die Funktion password_needs_rehash() ins Spiel. Diese gibt true zurück falls die übergebenen Parameter nicht mit denen des Hashes übereinstimmen. Dazu sind drei Dinge anzumerken:

D.h. wenn sich nun der Standardwert für den Algorithmus oder den Kostenfaktor ändert, können wir das nach dem erfolgreichen Login erfahren und direkt einen aktualisierten Hash für den Benutzer speichern.

Ich würde mir dann also sowas bauen:

function password_verify_default($password, $hash, callable $onNeedsRehashing) {
    if (!password_verify($password, $hash)) {
        return false;
    }

    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $newHash = password_hash($password, PASSWORD_DEFAULT);
        $onNeedsRehashing($newHash);
    }

    return true;
}

Die Anwendung wäre dann etwa so:

$pass = ...; // Given password
$user = ...; // User identified by given email, etc.
$em   = ...; // Entity manager

$onNeedsRehashing = function($hash) use ($user, $em) {
    $user->setPasswordHash($hash);
    $em->persist($user);
};

if (password_verify_default($pass, $user->getPasswortHash(), $onNeedsRehashing)) {
    // Yay.
} else {
    // Nay.
}

Ich finde den Schritt zu der neuen API super. So werden selbst unerfahrene Entwickler hoffentlich gar nicht erst auf die Idee kommen, Passwörter als ungesalzene MD5() Hashes zu speichern. +1 von mir. :)

Ähnliche Artikel

Kommentare