Entwickler-Ecke

Programmiersprachen (Server) - gesalzene Hashes


Heiko - Sa 29.11.08 00:49
Titel: gesalzene Hashes
Hallo,

es ist ja hinlänglich bekannt, dass man gängige "md5-Passwörter" mit Rainbowtables knacken kann. Abhilfe sollen da ja gesalzene Hashes schaffen - nur wie sehen da die gängige Vorgehensweisen aus?

Nun was ich zum salzen bis dato weiß ist lediglich, dass die Klartext-PWs werden dadurch länger->Bruteforce etc. braucht länger...

Bei der Implementierung scheitere ich aber momentan daran, dass ich bei einigen Salzungen noch nicht ganz durchsehe (phpBB3) und andere mir fraglich erscheinen.

z.B. dieser Skript hier [http://dev.splitbrain.org/reference/dokuwiki/nav.html?inc/auth.php.source.html#l836]: wenn man keinen salz angibt, generiert er jedesmal selber einen. Beim entschlüsseln liest er jedoch das Salz wieder aus - scheinbar aus dem Hash, soweit ich das verstanden habe :gruebel: .

Hat einer von euch da schon Erfahrung gesammelt?

Grüße
Heiko


BenBE - Sa 29.11.08 03:04

Ich geh bei Salted Hashes inzwischen folgenden Weg:

Jeder Hash besteht aus 3 Teilen:

  1. Dem Passwort-Hash
  2. Einem Salt-Wert pro User
  3. Einem Saltwert pro Programminstallation


Wobei weitere Informationen (die dem Server bekannt sind) mit einfließen können. Wichtig ist dabei, dass ich MD5 als Hash-Funktion meide (es gibt seit 2005 Papers, die zeigen, wie Kollissionen auf einfache Art gefunden werden können) und daher eher zu SHA1 bzw. SHA256 (Suhosin-Patch) tendiere. Eine Mischung verschiedener Hashfunktionen ist dabei durchaus möglich.

Ein Beispiel für eine Solche Verknüpfung könnte z.B. diese sein:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
$PWHash = sha256(
    $username . 
    sha1($password, true) . 
    sha1($system_salt, true) . 
    md5($username, true) . 
    sha256($password . $user_salt . $system_salt, true) . 
    $password);

Zur Erklärung, warum einige Informationen doppelt vorkommen: Um mehr Entropie in die Haupt-Schleife zu bekommen, da sonst zu wenig verschiedene Bits vorhanden sind, die in den Hashwert eingehen könnten. Hierbei sollte man bedenken, dass in jedem Schritt möglichst die doppelte Länge an Eingabe-Daten vorhanden sein sollte, wie der Hash nach durchführung des Rechenschrittes aufweist, da sonst keine Datenreduktion stattfindet.

Konkret: Wenn ich einen MD5-Hash, der 128 Bit lang ist habe, sollte ich mindestens 129 Bit, besser aber >=256 Bit als Eingabedaten bereitstellen, da sonst der Hash-Raum nicht vollständig ausgenutzt wird. Dies gilt insbesondere bei Kaskaden, wenn man verhindern möchte, dass man durch ungünstige Wahl der Verschachtelungsreihenfolge vorhersagbare Stellen erzeugt. Daher sollten soviele wie möglich variable Stellen innerhalb der Hash-Berechnung vorkommen. Auch sollte man die Binärausgabe der Hashes benutzen, da diese den vollen Ergebnis-Raum ausnutzt und somit der im Hash weiterverwendete Wert weniger Information aufweist (Die Hex-Ausgabe eines Hashes erlaubt voraussagen über die Konstellationen verschiedener Bits, die im Hex-Modus nicht möglich sind).

Auch hab ich schon auf manchen Seiten gesehen, dass Hash-Werte gekürzt ausgegeben werden, um die verwendete Hash-Funktion zu verschleiern. Aus sicht einer Rainbow-Table ist dies allerdings weder erschwerend, noch cryptographisch sinnvoll, da man damit die Chancen für Kollissionen erhöht. Auch Permutationen des Hashwertes haben wenig Sinn, da diese beim Berechnen von Kollissionen einfach berücksichtigt werden können.

Edit: Anm.: $user_salt und $system_salt sollten möglichst zufällige, mindestens 32 Byte umfassende Binärstrings sein, um in keinem der Kaskaden-Schritte einem Angreifer das Leben zu vereinfachen. Je Länger, desto besser. Und immer fein http://xkcd.com/221/ benutzen!


Heiko - Sa 29.11.08 11:18

Wo speicherst du den user_salt ab?


BenBE - Sa 29.11.08 14:37

Der UserSalt liegt bei mir als separate Spalte vor. Manche Hash-Algoritmen wie crypt und apr1-Hashes speichern diesen Teil am Anfang des Hash-Ergebnisses.


Sirke - Mi 03.12.08 21:41

Ich finde deine Ausführungen sehr gut und sie gehen auch auf den wichtigsten Teil von Salted-Hashs ein, aber eine Zeile ist kryptografisch sehr schlecht gewählt:

C#-Quelltext
1:
sha1($system_salt, true)                    

Hier erzeugst du einen 160Bit Hash von einer 256Bit Zufallszahl, welches beide Konstanten sind, sodass du effektiv deine gute, lange Zufallszahl eliminierst! Würde dort lieber das Salz im "Klartext" schreiben.

Gerade die Verbindung von $user_salt und $system_salt ist sinnvoll, da man das $user_salt in einer Datenbank und das $system_salt in einem PHP-Skript ablegt, sodass selbst ein Diebstahl der DB nicht dazu führt, dass die Passwörter gefunden werden können.


Wolle92 - Mi 03.12.08 23:54

Darf ich das ganze nochmal so wiedergeben, wie ich das verstanden habe?

Also:
Ich nehme mir Informationen, die dem Server bekannt sind (z.B. username), dann habe ich dazu noch ein System-Salz (In dem Salzstreuer mit systematischer Anordnung der Löcher), das im PHP-Script steht und ein User-Salz (In den entsprechenden Salzstreuern, deren Loch-Anordnung userspezifisch ist)...
Dann hänge ich den Kram aneinander, teilweise "vorgehasht" und bilde daraus dann einen endgültigen Passwort-Hash...

Soweit alles richtig?

Und dann noch die Frage: Könnte man nicht, damits nicht gleich auffällt, einfach das User-Salz z.B. aus dem Registrierungsdatum errechnen? Also einfach einen String wie 12-03-2008 auf Binärebene etwas auseinanderpflücken und lustig vertauschen (natürlich mit System, sonst wird das nix) und dann damit dem Hash salzen?

Grüße,
Wolle

PS: Eigentlich kann ich darauf verzichten, Hashs an sich schmecken schon nicht, und ich glaube, wenn man sie mit Nullen und Einsen salzt, wirds auch nicht besser...


Heiko - Do 04.12.08 00:04

user profile iconWolle92 hat folgendes geschrieben Zum zitierten Posting springen:
Und dann noch die Frage: Könnte man nicht, damits nicht gleich auffällt, einfach das User-Salz z.B. aus dem Registrierungsdatum errechnen? Also einfach einen String wie 12-03-2008 auf Binärebene etwas auseinanderpflücken und lustig vertauschen (natürlich mit System, sonst wird das nix) und dann damit dem Hash salzen?

Der Sicherheitsgewinn wäre imho dadurch kleiner. Nimm mal das DF: wieviele registrieren sich hier täglich? Sagen wir mal 10 Leute. Das würde heißen, dass 10 Leute den gleichen Userhash haben. Wenn du dagegen für jeden Benutzer einen zufälligen "Salzstreuer" nimmst, ist die wahrscheinlichkeit für gleiche Hashs geringer. Vorteil bei dir wäre aber, dass die DB kleiner wäre ;).


BenBE - Do 04.12.08 01:52

Cryptographisch gesehen ist etwas umso besser von der Sicherheit her, je weniger Zusammenhänge es zwischen den einzelnen Daten gibt. Daher ist ein vollständig zufällig gewählter Hash, der rein gar nichts mit dem User an sich zu tun hat, die beste Variante, ein zu diesem User gehöriges Passwort zu schützen, da der Salt in diesem Fall nicht erraten werden kann und somit gebruteforced werden müsste.

Zum Thema System Hash im Klartext: Mein Beispiel oben war nichts weiter als eine Möglichkeit. Manchmal bringt es schon etwas, Informationen zu reduzieren, da man somit bewusst Kollissionen erzeugt. Die bemängelte Zeile ist aber im besagten Beispiel unkritisch, da der System Salt 2 Zeilen weiter unten im Klartext verwendet wird und damit keine Datenreduktion im angesprochenen Sinne stattfindet.


matze - Do 04.12.08 11:20

Also meiner Meinung nach könnte man den Registrierungszeitpunkt durchaus verwenden. Wenn der als Timestamp abgespeichert ist, dann dürfe der ausreichend viele Stellen haben und quasi-zufällig sein.


BenBE - Do 04.12.08 12:06

Und genau das darf man nicht tun. Das haben schon große Software-Häuser falsch gemacht und damit Rainbow-Table-Angriffe wieder Lukrativ gemacht. Siehe http://marc.info/?l=bugtraq&m=120301051521503&w=2


Sirke - Do 04.12.08 19:10

Von der Methode mit dem Timestamp ist auf jeden Fall abzuraten, aber ich denke, dass eine Erweiterung einer Datenbank um ein 256Bit Feld auf keinen Fall ins Gewicht fällt ;-)

Ich persönlich verwende momentan mehrere Salze, welche ich in der Datenbank und im Skript ablege. Grund dafür sind jedoch keine Paranoia sondern spezielle Skripte die das nötig machen. Es reicht auch ein gesalzter Hash der Forum SHA256( Salz + SHA256( Salz + Passwort ) + Passwort ) aus, wobei das Salz mindestens die Ausgabelänge der Hashfunktion haben sollte.


matze - Fr 05.12.08 10:36

OK ich gebe mich geschlagen. Dann halt doch nicht der Timestamp. Wenn mir bitte aber jemand mal einfach erklären könnte, warum nicht, wäre das nett...


UGrohne - Fr 05.12.08 10:50

user profile iconmatze hat folgendes geschrieben Zum zitierten Posting springen:
OK ich gebe mich geschlagen. Dann halt doch nicht der Timestamp. Wenn mir bitte aber jemand mal einfach erklären könnte, warum nicht, wäre das nett...

Weil er vorhersagbar ist. Im Falle von htpasswd wäre es dann nicht so gut. Aber für die eigene User-Tabelle finde ich es besser, als den Salt extra noch in der User-Tabelle zu speichern, denn das ist offensichtlich. Dagegen andere statische Inhalte, wie das Anmeldedatum zu verwenden, finde ich persönlich hier passender, sonst kann man sich den User-Salt gleich sparen.


matze - Fr 05.12.08 14:03

Mit ist schon klar, dass es vorhersagbar ist und warum daraus ein Problem entsteht. Aber wie will der "Angreifer" denn an diesen Zeitpunkt herankommen?
Die genaue Zeit des Servers kann doch gar nicht bekannt sein. Also mit Millisekunden und Sekunden und so...


BenBE - Fr 05.12.08 15:58

Nehmen wir hier mal deinen Account im DF. Dieser ist am 30.06.2002 angelegt laut Profilinformationen. Wenn man jetzt noch etwas Social Engineering dazu nimmt, kann man seine Versuche daraufhin gewichten, dass der wahrscheinlich nachmittags oder Abends, unwahrscheinlich aber zwischen 3-6 A.M. angelegt wurde.

Was bringt mir das: Angenommen, der DF-Server würde die Sekunde deiner Registrierung zur Berechnung des Hashes heranziehen, so könnte ich für die in Fragekommenden Hashes Rainbow-Tables vorbereiten, mit denen ich gezielt diese Datensätze in dem von mir vermuteten Zeitraum angreifen kann. Dies gilt auch für andere Angaben, die man heranziehen könnte, wie Nutzername, Wohnort, ... All diese ermöglichen es, dass ich den möglichen Seed-Raum stark einschränken kann.

Zu deinem Punkt, cden Seed über alle Spalten zu berechnen: Das ist zwar möglich und identifiziert jeden User eindeutig, ist aber unhandlich, da für jede Profiländerung der Seed geändert wird und du damit dein Passwort neu eingeben kannst. Bei unverschlüsselten Verbindungen ersparst Du in diesem Fall dem Angreifer unter Umständen sogar das Bruteforcen, da er's eh bereits im Klartext bekommt ;-) Bei SSL bleiben Targeted Attacks ;-)

Wo liegt dann der Vorteil von User-Salts: Man kann das Passwort des Users salzen, ohne Informationen über diesen Salt herauszugeben. Und selbst wenn jetzt dieser User-Salt bekannt wird, bleibt immer noch ein System Salt, der dennoch die Attacke für das Vorausberechnen unmöglich macht. Bei User-basierten Hashes kann man durch eine entsprechende Initialisierung diesen Hash (zwar mit erheblichem Aufwand) ermitteln, in dem man Known-Plaintext auf die Passwörter geht und damit die Effekte des System Salts rausrechnen (diesen also ermittelt).

Und genau auf dieser Vorherberechenbarkeit der Salts basierte auch der Angriff auf das HTPasswd-Utility, da dieses nur im Sekundentakt neue Salts generiert hat. Wenn man also ausreichend genau (1-2 Tage) die Generierung des Saltwertes bestimmen konnte, so konnte man speziell für diesen Zeitraum gültige Rainbow-Tables erstellen.


Regan - Di 09.12.08 19:22
Titel: Schleichwerbung: http://zork3d.funpic.de/ Locusum-Forum
Ich hätte da mal eine Frage zu zwei Passwörtern und salted Hashes:
In wie weit bringen mir zwei Passwörter mehr Sicherheit, wenn ich die zwei Passwörter ineinander vermische und somit zwei neue Hashes erstelle. Ich gebe mal ein Beispiel:
Die Datenbank beinhaltet zwei Passwörter, welche zwei String unbestimmter Länge sind. Der Nutzer gibt nun zwei Passwörter ein. Ich vermische diese Passwörter nun mit verschiedenen Salzen, aber auch untereinander und erhalte so zwei Strings verschiedener Länge.
Kann dieser Vorgang von Erfolg gekrönt sein?
Habt ihr vielleicht Verbesserungsvorschläge?


Sirke - Di 09.12.08 20:44

Ich versuche mal schnell wiederzugeben, wie ich deine Idee verstanden habe: Du hast zwei Passwörter und ein Salz und vermischt alle drei Zeichenketten mit einander und errechnest dir anschließend daraus zwei neue!? Wird von diesen beiden Zeichenketten noch ein Hashwert gebildet?

Sofern - und davon gehe ich mal aus - ein Hashwert über diese beiden Zeichenketten gebildet wird, errecht man einen Zusammenhang der beiden Passwörter, sodass die Komplexität von beispielsweise Wörterbuchangriffen sich multipliziert und nicht wie bei seperaten Passwörtern addiert. Eine schlechte Implementierung kann jedoch genau das Gegenteil bewirken!

Eine wesentlich bessere Lösung wäre eher einen Hashwert für die zwei Passwörter zu wählen: sha256( salz0 . sha256( salz1 . passwort1 ) . sha256( slaz2 . passwort2 ) )
Bei einer Länge von 256 Bit je Salz würde sich für die innere Hash-Funktion der Output zum Input halbieren und für die äußere Hash-Funktion sogar dritteln. Eine Wahrscheinlichkeit zwei Passwörter zu finden, welche sowohl aus nur darstellbaren ASCII-Zeichen bestehen, als auch generell eine Kollision erzeugen, halte ich für sehr sehr gering!


Regan - Di 09.12.08 21:10

Vielen Dank erstmal für deine Antwort. Ich hab da noch eine Frage zu den Salzen: Müssen die vom Server sein, oder können die auch in der Datenbank liegen? Und: bringt ein zusätzliches Salz, das von Nutzer zu Nutzer unterschiedlich ist, noch was, wenn ich das hinten dran hänge (also an den ersten Hash und den zweiten Hash)?


BenBE - Di 09.12.08 22:03

Rein cryptografisch zählt für einen Angriff weniger die Datenmenge, die als Eingabe dient, sondern deren Entropie. Das ist insofern wichtig zu beachten, als dass ein Wörterbuch-Angriff gerade die geringe Entropie des Geheimnisses angreift. Wenn also ein Passwort allein ein Wort einer Wörterliste mit n Einträgen ist, so ist die Entropie dieses Passwortes ld(n) Bit. Würde man nun versuchen, ein Passwort zu encodieren, welches nicht bereits auf der Liste enthalten ist, so muss dieses Passwort auf der Liste ergänzt werden, wodurch die Anzahl der Einträge auf der Liste erhöht wird und damit die Datenmenge, die zum Eindeutigen Auffinden dieses Eintrags notwendig ist, steigt.

Somit ist es weniger eine Frage, ob ein Salt vorne oder hinten angehangen wird, sondern vielmehr, wieviele Informationen man verrät, wenn man die eine, oder die andere Möglichkeit wählt. Rein vom Aufbau eines Hash-Algorithmus darf anhand des Hashwertes kein Rückschluss auf das Argument der Hash-Funktion möglich sein. Somit ist es bei gleichmäiger Verteilung de Entropie unerheblich, wie die Eingabe aussieht; ist aber anzunehmen, dass der Salt wesentlich höhere Entropie-Dichten erzielt, wie das Passwort (und davon ist i.a.R. auszugehen, so muss man versuchen, die Position, an der diese Entropie einfließt zu verschleiern, da dies wieder eine Information des Ausgangswertes ist (ich hoffe, der letzte Satz ist soweit verständlich).

Vielleicht aber auch noch ein kleines Gedanken-Experiment: Ich habe einen Hash, dessen Eingabe aus der er berechnet wird, länger als er selbst ist. Ein Angreifer kennt für einen Hash das zughörige Passwort, jedoch nicht das Secret. Ist es nun günstiger, das Salt vorne oder hinten an das Passwort anzuhängen, und warum?
Vorne, weil hinten kann ich mir ne gane Menge Berechnungen sparen um den Salt herauszubekommen, da ich das Passwort nur einmal partiell hashen brauch und die Hashes iterativ prüfen kann. Dagegen müsste die Berechnung das Hashwertes bei einem vorangestellten Salt in jedem Fall neu erfolgen.
Wie ändert sich dies, wenn statt des Passwortes der Salt bekannt wäre?
Korrekt Sherlock: das vorangestellte Passwort währe nun hier besser ... ganz einfach, weil hier nun der gegenteilige Fall auftritt und die Vorherberechnung des Hashes möglich wir.

Somite ist es immer wichtig, wogegen der Salt schützen soll. Denn zuviel Salz schaded der Umwelt ;-)


Regan - Di 09.12.08 22:43
Titel: <a href="http://locusum.sf.net">Locusum besuchen!</a>
Aha, sehr interessant. Ich versuche mal, das Ganze jetzt zu summieren:
Also, zwei Passwörter sind besser als eines. Außerdem sollte man von beiden Hashes mit Salz bilden und beide Hashes dann kombinieren zu einem weiteren Hash. Es ist egal, ob ich ein Salz vorn oder hinten anhänge, denn wenn der Hacker das Salz weiß, dann findet er das Passwort raus; weiß er das Passwort, dann kennt er das Salz.
Ich denke, ich werde die Variante von user profile iconSirke nehmen und ein Salz vorne dranhängen. Diese scheint mir als sicher, vor allem sicherer als die nur mit einem Passwort.


BenBE - Di 09.12.08 22:59
Titel: Re: <a href="http://locusum.sf.net">Locusum besuchen!</a>
Nicht ganz:

user profile iconRegan hat folgendes geschrieben Zum zitierten Posting springen:
Es ist egal, ob ich ein Salz vorn oder hinten anhänge, denn wenn der Hacker das Salz weiß, dann findet er das Passwort raus; weiß er das Passwort, dann kennt er das Salz.

Beachte hier insbesondere, was ich zur Entropie gesagt habe: Wenn das mit viel Entropie vorne ist (meist der Salt-Wert), ist das Knacken des Passwortes für das Bruteforcen insgesamt schwieriger, da sehr weit vorne etwas steht, was bisher nicht bekannt ist. Daher sollte möglichst weit vorne bereits viel Entropie erzeugt werden, sodass möglichst wenige Informationen über die Eingabe erraten werden oder vorherberechnet\eingegrenzt werden können.


Regan - Di 09.12.08 23:08
Titel: Re: <a href="http://locusum.sf.net">Locusum besuchen!</a>
user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
Nicht ganz:

user profile iconRegan hat folgendes geschrieben Zum zitierten Posting springen:
Es ist egal, ob ich ein Salz vorn oder hinten anhänge, denn wenn der Hacker das Salz weiß, dann findet er das Passwort raus; weiß er das Passwort, dann kennt er das Salz.

Beachte hier insbesondere, was ich zur Entropie gesagt habe: Wenn das mit viel Entropie vorne ist (meist der Salt-Wert), ist das Knacken des Passwortes für das Bruteforcen insgesamt schwieriger, da sehr weit vorne etwas steht, was bisher nicht bekannt ist. Daher sollte möglichst weit vorne bereits viel Entropie erzeugt werden, sodass möglichst wenige Informationen über die Eingabe erraten werden oder vorherberechnet\eingegrenzt werden können.

Das stimmt, aber wir wollen es ja nicht zu schwer machen :wink: .


Heiko - Mi 10.12.08 00:12
Titel: Re: <a href="http://locusum.sf.net">Locusum besuchen!</a>
user profile iconRegan hat folgendes geschrieben Zum zitierten Posting springen:
Also, zwei Passwörter sind besser als eines.

Doch eigentlich auch nur bedingt, oder? Die zusätzliche Sicherheit kommt imho ja auch nur dadurch zu stande, dass mehr Möglichkeiten existieren. Das könnte man imho durch längere Passwörter kompensieren. Oder sehe ich das falsch?


BenBE - Mi 10.12.08 01:54

Das ist soweit korrekt. Man kann aber auch ein gutes, statt zwei schlechten Passwörtern vergeben ;-)