Sichere Passwort-Hashes
Bei der Speicherung von Passworten in Datenbanken sind hohe Sicherheitsanforderungen gestellt. MD5-Hashes sind schnell geknackt, und iterierte Hashes – auch in Kombination mit verschiedenen kryptografischen Algorithmen (z.B. SHA1 mit MD5) – erhöhen die Kollisionswahrscheinlichkeit. Die Lösung des Problems liegt im „Würzen“ des Hashes, im Fachjargon „Salt“ (Salz) genannt. Durch Beigabe eines Zufallswertes, der an die eigentlichen Ausgangsdaten angefügt wird, entsteht ein nicht rekonstruierbarer Hashwert (auch Salted Hash genannt). Dieser ist deutlich sicherer und resistent gegen Attacken mittels Rainbow-Table. Allerdings muss auch hier etwas getrickst werden, um für Sonderfälle gewappnet zu sein. Wer sich tiefgehender mit diesem Thema befassen möchte, findet z.B. hier gute Fachliteratur zum sicheren Programmieren.
Das Problem
Die gängige Praxis sieht meistens noch so aus:
$password = md5($password);
Das ist die schnellste aber auch eine unsichere Lösung. Denn durch Bruteforce- bzw. Wörterbuch-Attacken sind solche Hashes schnell geknackt. Es gibt bereits Internetseiten, die sog. Rainbow Tables anbieten. Das sind fertige Wörterbuchlisten mit bereits vorberechneten Hashes. Ganz kluge Köpfe hashen nun einfach mehrfach und in Kombination mit verschiedenen Algorithmen:
$password = sha1(md5(md5(sha1(md5($_POST['password'])))));
Doch dieses Vorgehen bringt keine Besserung, ganz im Gegenteil. Der ursprüngliche Plaintext (z.B. das Passwort) hat eine zufällige Länge und auch die vorkommenden Zeichen sind zufällig. Allerdings bereits durch den ersten Hashvorgang wird sowohl die Länge als auch der Zeichensatz beschränkt, was die Kollisionswahrscheinlichkeit erhöht. Kurz gesagt, einmaliges Hashen ist sicherer als das mehrfache.
Die Lösung
Und hier kommt das Salz ins Spiel. Man erstelle einen dynamischen Zufallswert wie z.B. den aktuellen Zeitstempel, und hänge ihn an das Klartext-Passwort an:
$salt = microtime();
$password = 'meinpasswort';
$hashed_password = sha1($password.$salt);
Dieser Weg ist schon deutlich sicherer als das oft praktizierte Standard-Vorgehen. Allerdings hat diese Lösung einen kleinen Schönheitsfehler. Denn der Zufallswert muss natürlich zusätzlich in der Datenbank gespeichert werden, um beim Login des Benutzers den Hashwert generieren zu können. Gelangt der Bösewicht an die Datenbank, besitzt er natürlich auch den Zufallswert. Durch einen Datenbank-Klau wäre man also wieder am Anfang, da der Faktor des zufälligen Wertes wegfallen würde.
Also erweitern wir das Rezept ein wenig und fügen zu dem dynamischen Zufallswert noch einen statischen Zufallswert hinzu. Dieser wird am besten irgendwo in einer Konfigurationsdatei gespeichert. Nun entsteht ein sehr sicheres Konstrukt. Das Passwort wird mit dem dynamischen Zufallswert aus der Datenbank sowie mit dem in einer Datei gespeicherten statischen Zufallswert kombiniert:
$dynamic_salt = microtime();
$static_salt = 'TrSeEUiSrAkykk4sEniyP67Q2BTp8vtDqoqw';
$password = 'meinpasswort';
$hashed_password = sha1($dynamic_salt.$password.$static_salt);
Diese Vorgehensweise hat zwei Vorteile: 1. Selbst im Falle eines Datenbank-Diebstahls, würde immer noch der statische Zufallswert fehlen. 2. Zwei gleiche Passworte ergeben trotzdem unterschiedliche Hashes, wodurch keine Rückschlüsse gezogen werden können.
Diese Lösung stellt keinesfalls das Ende der Fahnenstange dar. Es kursieren einige Weiterentwicklungen im Netz, bei denen z.B. der Zufallswert nicht vollständig in der Datenbank gespeichert oder noch anderweitig kombiniert wird. Der eigenen Kreativität sind dabei kaum Grenzen gesetzt. Die hier dargestellte Lösung kann aber zu Recht als extrem sicher eingestuft werden, und das bei nur geringem Mehraufwand.
scheffe007
12. September 2008 um 23:01 Uhr
Sehr einseitige Darstellung.
Fakt bleibt doch: als Entwickler oder Admin einer solchen Datenbank hat man Zugriff zu Hash, dynamischem und statischem Salt.
Und was dann?
Ich würde sagen: viel Aufwand für (fast) nix.