Passwort-Hashes und Systemsicherheit

Inzwischen hat es sich rumgesprochen, dass man keine Passwörter in Plaintext speichern soll. Das stellt ein grobes Sicherheitsrisiko dar, da nicht davon ausgegangen werden kann, dass die Datenbank sicher vor fremden Zugriff hat. Nichts ist peinlicher, als dass man zugeben muss, dass Passwörter in Reintext geklaut werden konnten.

Deshalb speichern nahezu alle verfügbaren Systeme ein Passwort kodiert. Das geschieht meist mit einem md5, seltener auch mit einem SHA1-Hash:

[source:php]
$passwordEncoded = md5($password);
[/source]

Da es sich bei Hash-Verfahren um Einweg-Verschlüsselungen handelt, ist es damit nicht möglich, direkt auf das Original-Passwort zu gelangen. Wunderbar könnte man meinen. Leider hat man dann nicht mit der Energie krimineller Täter gerechnet. Denn neben Brute-Force-Berechnungen gibt es noch ganz andere Möglichkeiten, auf die Original-Passwörter zu kommen. Das müssen keineswegs komplexe Rechenoperationen sein. Viel einfacher ist die Verwendung spezieller Datenbanken, die zu vielen Ausdrücken die entsprechenden Hashes vorweisen können.

Eine Lösung dieses Sicherheitsproblems besteht darin, dem Benutzer einzubläuen, dass er auf jeder Seite unterschiedliche Passwörter verwenden soll. Dass das nicht funktioniert, dürfte bekannt sein. Eine andere intelligentere Lösung besteht darin, SALTs zu verwenden. Dabei handelt es sich um meist zufällig generierte String-Muster, die bei der Berechnung des Hashes mitbenutzt werden. Dadurch ist die Verwendung von Datenbanken zur Rückkodierung der Passwörter sinnlos. Eine passende PHP-Funktion namens crypt ist auch in der Lage genau das zu tun.

[source:php]
/*
* Ohne Angabe wird theoretisch ein zufälliger Salt-Wert generiert (ACHTUNG, das ist nicht immer so und hängt von der Serverkonfiguration ab
*/
$encryptedPassword = crypt($password);

[/source]

Das Problem ist hier, dass je nach Serverkonfiguration ein SALT zufällig berechnet wird oder fest vorgegeben ist. Die Idee hinter der Verwendung des SALT-Wertes ist es jedoch gerade, dass keine Datenbank dafür möglich ist. Sobald jedoch eine Standard-Eingabe verwendet wird, verblasst dieser Effekt. Deshalb sollte hier gezielt darauf geachtet werden, dass ein zufälliger SALT-Wert verwendet wird.

Die Überprüfung eines Passwortes ist recht einfach. Übergibt man als SALT-Wert das bisherige Passwort, so wird der verwendete SALT automatisch ausgelesen (meistens die ersten Buchstaben).

[source:php]
if (crypt($password,$oldPassword) == $oldPassword) {
//Passwort stimmt
}
[/source]

Es existiert auch eine passende mySQL-Funktion namens ENCRYPT, so dass der Vergleich auch auf der Ebene der Datenbank möglich ist. Jedoch sollte von dieser Passwort-Überprüfung Abstand genommen werden, da für jede Datenbankzeile die crypt-Funktion ausgeführt werden muss. Sinnvoller ist es da, nur nach dem Benutzernamen die Zeilen abzufragen und dann im Code das Passwort einmal zu überprüfen.

Die Verwendung von SALT und Hashes ermöglicht die relativ sichere Speicherung von Passwörtern und bietet dem Benutzer maximal möglichen Schutz.

3 Gedanken zu „Passwort-Hashes und Systemsicherheit

  1. Hallo Mathias,
    guter Artikel, danke dafür.
    Eine Frage dazu.
    Angenommen man hat ein User Login System. Der Username und das Passwort wird in eine MySQL DB gespeichert. Das Passwort wird wie oben beschrieben mit “# $encryptedPassword = crypt($password);” gespeichert.
    Du schreibst es werden hier zufällige SALT Werte verwendet.
    Kann man dann auch wieder mit “crypt()” den Hash Wert vergleichen?
    Also etwa.
    SELECT userdata, UserID FROM usertable WHERE usr='”.$USER.”‘ && pass='”.crypt($_POST[‘PWD’]).” ….

    Gruss
    hawk

  2. Nein, das geht im Allgemeinen nicht. Der SALT-Wert sollte bei jeder Anwendung von crypt ohne SALT zufällig sein, womit stets ein anderer Hash-Wert rauskommt. Genau das ist ja Sinn und Zweck der Methode. Leider ist das nicht auf allen Systemen so. Der SALT, der beim Abspeichern generiert wurde, lässt sich aber immer aus dem Hash auslesen. Das funktioniert eben mit

    [source:php]
    $passwordCheck = crypt($enteredPassword,$savedPassword);

    if ($passwordCheck==$savedPassword) {
    //Passwort ist richtig
    }
    [/source]

    Der verwendete SALT steht in der Regel am Anfang des Hashes und hat je nach installiertem Verfahren eine unterschiedliche Länge. Er ist also öffentlich sichtbar. Da er aber zufällig ist, helfen Datenbanken nicht.

    Zurück zu deiner Frage: Du kannst den PHP-Befehl crypt also nicht in die mySQL-Abfrage einbauen. Statt dessen musst du die ENCRYPT-Funktion von mySQL verwenden, welche exakt gleich funktioniert (und im Hintergrund ebenfalls den Linux-Befehl crypt verwendet). In deinem Beispiel also:

    [source:sql]
    SELECT userdata, UserID FROM usertable WHERE usr=$USER && pass=ENCRYPT($enteredPassword,pass)
    [/source]

    Du verwendest also die Spalte pass um den verwendeten SALT zu ermitteln. Das solltest du jedoch auf keinen Fall machen, da die ENCRYPT-Funktion für jede Zeile ausgeführt werden muss. Also frag nur den Benutzernamen ab und überprüf dann mit PHP geziehlt das Passwort. Dann muss die Funktion crypt nur einmal ausgeführt werden. Das ist wesentlich performanter!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.