Logo

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.

Nachtrag vom 18.02.2013: Passworte sollten nicht mehr als MD5- oder SHA1-Hashes abgespeichert werden, da dies nicht mehr als sicher gilt. Stattdessen sollte ein geeignetes Verfahren wie bcrypt oder PBKDF2 verwendet werden.

6 Kommentare

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.

Aufschnürer
13. September 2008 um 16:09 Uhr

Scheffe007, was sollte an diesem Artikel einseitig sein? Also, Fakt ist: Für einen ungesalzenen Hash muss man sich noch nicht mal weitere Mühen machen, man kann munter mit der Bruteforce-Attacke beginnen. Um an dynamischen und statischen Salt zu gelangen muss der Angreifer nicht nur Zugriff auf die Datenbank sondern auch auf den Webserver/FTP haben. Der Einsatz dynamischer und statischer Salts erschwert es Angreifern deutlich. Besser, als die Hashes gar nicht zu salzen.

nico
21. Oktober 2008 um 19:36 Uhr

scheffe007 hat meine Meinung nach Recht. Die Bruteforce Zeit wird nicht effektiv verlängert. Letztendlich hängt es immer noch vom Passwort selbst ab, wie lange ein solcher Angriff dauern würde, denn beide Werte sind statisch und daher volkommen irrelevant. Sagen wir, man hat einen Benutzernamen und will per Bruteforce (eine schlechte Methode…) an sein Passwort kommen, das selbst programmierte Skript muss ja den zum Benutzer passenden salt anhängen, und static_salt ist ja auch immer gleich. Der Angreifer braucht beide gar nicht zu kennen. Das macht das selbst erstellte Skript schon ganz von alleine.

Gut jedoch der Hinweis mit dem doppeltem Hashen!

Aufschnürer
21. Oktober 2008 um 21:41 Uhr

Deswegen wie gesagt zusätzlich zum statischen Salt einen dynamischen verwenden. Dieser ist eben NICHT jedesmal gleich sondern wird beim Erstellungszeitpunkt zufällig gewählt.

Wenn man allerdings eine Login-Seite automatisch per Script ansteuern kann, bieten die Salts keine zusätzliche Sicherheit. Mehr Sicherheit bieten die Salts nur wenn man versucht, die Hashes direkt zu cracken.

Eins ist sicher und da sind wir uns einig: Mit der Passwortstärke steht und fällt die Sicherheit!

CharlySan
6. Januar 2009 um 16:16 Uhr

Also ich kann Aufschnürer nur recht geben, dieses Methode ist absolut sinnvoll. Natürlich nützt sie nichts bei einem BruteForce Angriff, vor dem man sich sowieso ganz anders schützen sollte, abgesehen davon, dass dieser eh nicht sehr erfolgversprechend ist, allerdings schützt es bestens vor den viel gefährlicheren Rainbow Tables. Ich kann aus eigener Erfahrung sagen, dass mithilfe von diesen wirklich leicht Passwörter geknackt werden können.
Guter Artikel, auch wenn das eigentlich Basiswissen sein sollte, aber hilft zumindest den vielen Anfängern (von denen es neuerdings etliche gibt, wenn’s ums Webprogrammieren geht).

Gruß, CharlySan

dumkopf
3. März 2011 um 22:19 Uhr

Ja ich bin etwas spät, aber gerade darüber gestoleprt weil ich schauen wollte obs noch super-sichere ahsing algos gibt 512 bit lange oder so.
also ih pfosten, auch beim bruten bringt der salt was. Zumindest wenn man jedem user einen eigenen salt zuweist. auf diese art bringt das gegen bruten auf einen user zwar nichts, aber man muss jeden user einzeln bruten und kann nicht mehr ganze passwort listen bruten.



Sag' deine Meinung