In diesem Artikel wird eine sichere Implementierung zum Speichern von Passwörtern in Datenbanken vorgestellt.
Theorie
Zunächst sollte man bedenken, dass Passwörter keinesfalls im Klartext gespeichert werden dürfen. In der Vergangenheit wurden des öfteren Sicherheitslücken in Datenbanken genutzt, um Passwörter auszuspionieren.
Daher ist es ratsam Passwörter in verschlüsselter Form in der Datenbank abzulegen. Die einfachste Methode ist das bilden der MD5-Prüfsumme des Passwort-Strings.
Das Passwort "testpasswort" würde also wie folgend in Form eines 128-Bit Hashes abgespeichert werden:
e16b2ab8d12314bf4efbd6203906ea6c
In PHP würde dies wie folgt durchgeführt werden:
<?php
$password_hash = md5("testpasswort");
?>
Verschlüsselung vs Hash
Bei MD5 oder SHA von einer Verschlüsselung zu reden ist genau betrachtet falsch. Eine Prüfsumme dient zur Verifizierung der Integrität eines Objektes (z.B. eine Zeichenkette) und ist nicht umkehrbar.
Eine echte Verschlüsselung ist immer umkehrbar. Besitzt man den passenden Schlüssel, kann also die ursprüngliche Information dargestellt werden.
Der Unterschied wird oft nicht beachtet, weshalb ich im folgenden der Einfachheit halber von Verschlüsselung rede.
Die MD5 Methode wurde des öfteren geknackt und gilt somit als weniger sicher. Daher wird meist der Secure Hash Algorithm verwendet.
Hier gibt es Unterschiede bei den Hash-Größen. Die zwei wichtigsten sind hier SHA256 und SHA512. Die Zahl hinter SHA steht hierbei für die Größe des Hashes (z.B. 512-Bit).
In der Regel genügt ein 256-Bit Hash. Wer auf Nummer sicher gehen will, kann auch den 512er Hash verwenden. Allerdings sollte man beachten, dass mit zunehmender Hash-Größe auch die nötige Rechenleistung zum verschlüsseln steigt. Bei einem großen Besucheraufkommen kann es hier zu Problemen kommen.
Nun zur Implementierung in PHP. Glücklicherweise bietet PHP wie auch für MD5 eine eigene Funktion an.
<?php
$password_hash = hash('sha256', 'testpasswort');
?>
Die Variable $password_hash besitzt anschließend den folgenden Wert.
94ac5813105c9887c263df59d0145af2b4df7b49969a08fba624e55eec25d6b1
Zusätzliche Sicherheit mit einem Salt
Um es Angreifern noch schwieriger zu machen, kann man das Passwort mit einem Salt versehen. Man streut also zusätzlich Salz in die Suppe bzw. das Passwort.
<?php
$secret_salt = "topsecretsalt";
$password = "testpasswort";
$salted_password = $secret_salt . $password;
$password_hash = hash('sha256', $salted_password);
?>
Dies würde folgendes Ergebnis liefern:
a77fb85ed091a4a2c677d558a253cbff1f11c84abf5076cd9a1bf0d623e04170
wobei das eigentliche Passwort des Benutzers als SHA256 Hash einen komplett anderen Wert besitzt.
Sollte ein Angreifer also den 256-Bit Hash knacken, was bisher noch nicht einmal der geheimsten Organisation mit 3 Buchstaben gelungen ist, dann hat er erst das "versalzene" Passwort. Dementsprechend sollte man gerade beim Salt keine eindeutigen Zeichenketten verwenden.
Platzbedarf in Datenbanken
Um den Passwort Hash nun in die Datenbank zu legen, sollte man sich zunächst einige Gedanken über die Größe und damit den Datentyp machen.
SHA256 liefert einen 256-Bit Hash zurück. Bei 4 Bit pro Zeichen entspricht das einem VARCHAR mit einer Länge von 64.
Bei der Verwendung von SHA512 beträgt die Länge des VARCHAR 128.
Praktischer Nebeneffekt bei gehashten Passwörtern: die Größe des Strings ist immer gleich. Man muss also in der Datenbank keine riesigen Felder reservieren bzw. die Passwortlänge der Benutzer nicht einschränken.
Passwort überprüfen
Um eine Passworteingabe des Benutzer zu überprüfen muss der eingegebene String wieder auf die gleiche Weise versalzen und verschlüsselt werden. Anschließend wird der neue Hash mit dem aus der Datenbank verglichen.
<?php
function encrypt_password($password) {
$secret_salt = "topsecretsalt";
$password = "testpasswort";
$salted_password = $secret_salt . $password;
$password_hash = hash('sha256', $salted_password);
return $password_hash;
}
// Benutzername
$username = "foxplex";
// Vom Benutzer eingegebenes Passwort
$user_password = "testpasswort";
// Passwort des Benutzers verschlüsseln
$user_password_hash = encrypt_password($user_password);
// Benutzer und Hash prüfen
$sql = "SELECT userid FROM users WHERE username = '$username' AND password_hash = '$user_password_hash'";
$result = $db->query($sql);
if ($result->num_rows == 1) {
// Benutzername und Passwort sind korrekt
echo "User authenticated";
exit(0);
} else {
// Benutzername oder Passwort sind falsch
echo "Wrong username or password";
exit(1);
}
?>
Fazit
Im Umgang mit Passwörtern ist Vorsicht geboten. Dank bewährten Verschlüsselungsverfahren wie dem SHA und den bereits vorhandenen Funktionen in PHP ist es sehr einfach möglich, sichere Webanwendungen zu konstruieren.
Lukas
23.08.2017 14:50
Ich habe das ganze so gemacht, dass das beim Login der Salt neu gemacht wird und auch in der Datenbank gespeichert wird. Somit ist selbst der Salt komplett Random und bei jedem Login neu gemacht.
Hier mein Code:
//Starte die Session
session_start();
//Global Config einbinden
require('global_config.php');
//Datenbankverbindung prüfen
$con = mysqli_connect(host, user, pass, db);
$user_id = '1';
$get_user = mysqli_query($con, "SELECT `username`, `salt`, `password_hash` FROM `blox_user` WHERE `user_id` = '$user_id'");
$gu = mysqli_fetch_array($get_user);
$salt = $gu['salt'];
$passwort = "test123";
$salted = $salt.$passwort;
$password_hash = hash('sha256', $salted);
echo $password_hash."<br><br>= ";
echo $gu['password_hash'];
$new_salt = rand(111111111,999999999);
$new_salted = $new_salt.$passwort;
$new_hash = hash('sha256', $new_salted);
echo "<br><br>Nächster Hash = ";
echo $new_hash;
$update_salt = mysqli_query($con, "UPDATE `blox_user` SET `salt` = '$new_salt', `password_hash` = '$new_hash'");
Ayna
10.01.2017 17:01
Wenzel
06.06.2016 16:11
Jan Gregor Triebel
11.06.2012 13:28
Der Vorteil liegt darin, dass auch bei einem bekannten Salt keine Hash Tabelle berechnet werden könnte, bzw für jeden User neu berechnet werden müsste, was den Aufwand unrentabel macht.