Worum geht's hier?

In zwei Jahrzehnten als Vollzeitnerd hat sich der ein oder andere Text angesammelt, mit denen ich unseren Azubis versucht habe, Grundlagen über unseren Job (TCP/IP, aber auch bevorzugt Telefonie) zu erklären.

Das stelle ich hier, wann immer ich so einen Text nochmal irgendwo finde, zusammen. Diese Texte haben nicht den Anspruch, zu 100% exakt zu sein (sondern manches ist des besseres Verständnisses wegen vereinfacht worden). 

Heute:

Passwort-Sicherheit mit MD5, salted passwords, RainbowTables usw.

Passwörter von Dritten (also z.B. von meinen Anwendern, wenn ich irgend eine Software programmiere) gehören nicht im Klartext in einer Datenbank gespeichert. Die sind vertraulich und sollten nur dem Anwender bekannt sein, also gehen sie auch mich nichts an (und auch niemand anderen, der gewollt oder aufgrund eines Sicherheitsproblems in meine Datenbank gucken kann). 

Aber irgendwie muss ich ja überprüfen, ob der Benutzer das richtige Passwort kennt. Wie soll ich das tun, wenn ich das Passwort selbst nicht kenne?

Ganz einfach: In dem ich mir einen Hash-Wert merke, eine Art Prüfsumme.

Sagen wir mal, das Passwort lautet: gehEiM123Vier.

Dann erfinde ich nun ein Prüfziffernverfahren, das da lautet: Anzahl Buchstaben insgesamt, Anzahl Großbuchstaben, Anzahl Vokale, Anzahl Zeichen überhaupt, Summe aller Ziffern.

Also: 10 Buchstaben, 3 Großbuchstaben, 4 Vokale, 14 Zeichen, Summe aller Ziffern ist 6. Kurzform: 1034146

Und diesen Hashwert 1034146 speichere ich mir in meiner Datenbank ab. 

  • Vorteil 1: Ich kann jederzeit nachrechnen, ob derjenige das richtige Passwort kennt. Wenn sich jemand mit Passwort Test123 einloggen will, ermittle ich daraus auch den Hashwert (er lautet 41176) und weil die Hashwerte unterschiedlich sind, muss das Passwort falsch sein.
  • Vorteil 2: Obwohl ich so das Passwort prüfen kann, habe ich das eigentliche Passwort nirgendwo gespeichert. Und es ist auch unmöglich, aus dem gespeicherten 1034146 jemals wieder das Passwort gehEiM123Vier. zurückzurechnen. (Darum nennt sich das Verfahren auch Einwegverschüsselung, es gibt kein zurück).
  • Nachteil: Mein Hash-Verfahren ist leider zu ungeau. Es gibt zu viele andere Passwörter (z.B. blaUKRaut222d), die auch funktionieren würden, weil sie den gleichen Hashwert ergeben.

Also brauchen wir einen eindeutigeres Verfahren. Und da war lange Zeit md5 sehr beliebt. Mit MD5 kann ich aus einer beliebigen Zeichenkette einen 32 Zeichen lange Hashwert generieren, der ausreichend genau ist (ähnlich wie ein Fingerabdruck ... weshalb man neben "Hash" auch von "Fingerprint" redet). Wie sowas aussieht, kann man z.B. bei https://www.md5-generator.de/ ausprobieren.

MD5 ist auch heute noch sehr beliebt, um bei großen Datenmengen (z.B. Downloads) zu gucken, ob auf beiden Seiten der Übertragung der gleiche md5-Hash rauskommt (wenn ja, war der Transport fehlerfrei). Sein großer Vorteil ist, dass es mit wenig Rechenleistung ermittelt werden kann (das geht schnell und ist z.B. auch auf Kleinstsystemen möglich).  

Für Passwörter sollte es aber nicht mehr verwendet werden. Denn es gibt:

Rainbow-Tables

Gerade, weil MD5 mit recht wenig Rechenaufwand ermittelt werden kann, kann man auch schnell verschiedene Passwörter durchprobiere (brute-force) und in eine sog. Rainbow-Table schreiben. Diese ermittelten Tabellen aus probiertem-Passwort und ermitteltem MD5-Hash hat irgendwann Googe gefunden. Und wenn man heute in einer Datenbank e8636ea013e682faf61f56ce1cb1ab5c findet, dann kann man zwar mathematisch das Passwort nicht zurückrechnen. Aber eine einfache Google-Suche zeigt, dass das alles andere als geheim ist. 

Salted MD5

Will/Muss man trotzdem mit MD5 arbeiten, dann lautet die Option: salted md5. Man verfeinert das eigentliche Geheimnis mit einer zufällig gewählten Prise Gewürz. Also das Passwort aus dem vorherigen Beispiel ist weiterhin "geheim", aber bevor wir es errechnen, kleben wir einfach immer "@vollzeitnerd.de" hinten dran. Und ein MD5("geheim@vollzeitnerd.de") ist dann 6589727c9d4d41ecea1b1fd924f22187, und das findet man in keiner Rainbow Table - es ist sicher.

Das allerdings wäre gar kein Salt, sondern ein Pepper. Alle auf meinem Server gespeicherten Passwörter sind so gepfeffert. Und solange ich keinem das Rezept (mein Pepper) verrate, ist das absolut sicher. Wenn aber z.B. alle TYPO3- oder Wordpress-Installationen weltweit das gleiche Pepper gewählt hätten, hätte man sich zu früh gefreut: Dann wäre die Motivation für Dritte zu groß, einfach neue Rainbow-Tables für Passwörter mit diesem vorangestelltem Pepper zu errechnen.

Einen Schönheitsfehler gäbe es dabei weiterhin: Haben zwei Benutzer das gleiche Passwort, dann haben sie auch den gleichen Hashwert in der Datenbank. Zwar kann man es, wenn es gepfeffert wurde, nicht zurück"rechnen". Aber alleine schon zu wissen, dass es gleich ist, ist ein Sicherheitsproblem.

Und da kommt das Salz dazu. Ein Salt ist, genau wie ein Pepper, ein zufälliger Wert, der an das Passwort rangeklebt wird (und dadurch dessen MD5-Hash verändert). Aber: Salt bekommt jeder User eine andere Prise (die man sich dazu natürlich merken muss) - z.B. einfach die User-ID. Und wenn der User ID 1 als Passwort "geheim" wählt, dann wäre sein Hash MD5("geheim@vollzeitnerd.de:uid1") beispielsweise 39f111a74021fad6bf7d0c0e9d1024f4, beim User mit der ID 2 hingegen 96628523d72627b8bf5bf1be7a8f691b - und es ist nicht mehr erkennbar, dass die beiden das gleiche Passwort haben.

Alternativen

Ein salted und peppered MD5 ist zum Speichern von Passwörtern absolut in Ordnung. Trotzdem gibt es noch ein paar komplexere (bei denen salt/pepper dann auch schon in der Implementierung vorgesehen ist, sich der Entwickler also nicht mehr selbst drum kümmern muss) Hash-Verfahren. Das Grundprinzip ist aber das gleiche.

Feedback