Wie kann bcrypt eingebaute Salze haben?

617

In Coda Hales Artikel "So speichern Sie ein Passwort sicher" heißt es:

In bcrypt sind Salze eingebaut, um Regenbogentischangriffe zu verhindern.

Er zitiert dieses Papier , das besagt, dass in der OpenBSD-Implementierung von bcrypt:

OpenBSD generiert das 128-Bit-bcrypt-Salt aus einem arcfour-Schlüsselstrom (arc4random (3)), der mit zufälligen Daten versehen ist, die der Kernel aus Geräte-Timings sammelt.

Ich verstehe nicht, wie das funktionieren kann. In meiner Vorstellung von einem Salz:

  • Es muss für jedes gespeicherte Passwort unterschiedlich sein, damit für jedes eine separate Regenbogentabelle generiert werden muss
  • Es muss irgendwo gespeichert werden, damit es wiederholbar ist: Wenn ein Benutzer versucht, sich anzumelden, versuchen wir es mit seinem Kennwort, wiederholen den gleichen Salt-and-Hash-Vorgang wie bei der ursprünglichen Speicherung seines Kennworts und vergleichen

Wenn ich Devise (einen Rails-Anmeldemanager) mit bcrypt verwende, befindet sich keine Salzspalte in der Datenbank, daher bin ich verwirrt. Wenn das Salz zufällig ist und nirgendwo gelagert wird, wie können wir den Hashing-Prozess zuverlässig wiederholen?

Kurz gesagt, wie kann bcrypt eingebaute Salze haben ?

Nathan Long
quelle

Antworten:

789

Das ist bcrypt:

Erzeugen Sie ein zufälliges Salz. Ein "Kosten" -Faktor wurde vorkonfiguriert. Sammeln Sie ein Passwort.

Leiten Sie einen Verschlüsselungsschlüssel aus dem Kennwort unter Verwendung des Salz- und Kostenfaktors ab. Verwenden Sie diese Option, um eine bekannte Zeichenfolge zu verschlüsseln. Speichern Sie die Kosten, das Salz und den Chiffretext. Da diese drei Elemente eine bekannte Länge haben, ist es einfach, sie zu verketten und in einem einzigen Feld zu speichern, sie jedoch später aufzuteilen.

Wenn jemand versucht, sich zu authentifizieren, rufen Sie die gespeicherten Kosten und das gespeicherte Salz ab. Leiten Sie einen Schlüssel aus dem eingegebenen Passwort, den Kosten und dem Salz ab. Verschlüsseln Sie dieselbe bekannte Zeichenfolge. Wenn der generierte Chiffretext mit dem gespeicherten Chiffretext übereinstimmt, stimmt das Kennwort überein.

Bcrypt funktioniert sehr ähnlich wie herkömmliche Schemata, die auf Algorithmen wie PBKDF2 basieren. Der Hauptunterschied besteht in der Verwendung eines abgeleiteten Schlüssels zum Verschlüsseln von bekanntem Klartext. Andere Schemata gehen (vernünftigerweise) davon aus, dass die Schlüsselableitungsfunktion irreversibel ist, und speichern den abgeleiteten Schlüssel direkt.


In der Datenbank gespeichert, könnte ein bcrypt"Hash" ungefähr so ​​aussehen:

$ 2a $ 10 $ vI8aWBnW3fID.ZQ4 / zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

Dies sind eigentlich drei Felder, die durch "$" begrenzt sind:

  • 2aIdentifiziert die verwendete bcryptAlgorithmusversion.
  • 10ist der Kostenfaktor; 2 10 Iterationen der Schlüsselableitungsfunktion werden verwendet (was übrigens nicht ausreicht. Ich würde Kosten von 12 oder mehr empfehlen.)
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTaist das Salz und der Chiffretext, verkettet und in einer modifizierten Base-64 codiert. Die ersten 22 Zeichen werden auf einen 16-Byte-Wert für das Salt dekodiert. Die verbleibenden Zeichen sind Chiffretext, der zur Authentifizierung verglichen werden soll.

Dieses Beispiel stammt aus der Dokumentation zur Ruby-Implementierung von Coda Hale.

erickson
quelle
7
Möchten Sie mehr darüber erfahren, warum ein Kostenfaktor von 10 nicht ausreicht? In Grails habe ich festgestellt, dass 10 der Standardwert für Kostenfaktor- / Protokollrunden für bcrypt ist, sodass es sich aufgrund Ihres Vorschlags möglicherweise lohnt, ihn zu aktualisieren.
pm_labs
57
Der Kostenfaktor für bcrypt ist exponentiell, oder besser gesagt, ein Kostenfaktor von 10 bedeutet 2 ^ 10 Runden (1024), ein Kostenfaktor von 16 würde 2 ^ 16 Runden (65536) bedeuten. Dann ist es natürlich, dass es 5-10 Sekunden dauern würde. Es sollte ungefähr 64-mal so lange dauern wie ein Kostenfaktor von 10. Um andere Fehlinformationen zu beseitigen, verwendet die Kryptofunktion von PHP die in c implementierte Unix-Kryptobibliothek.
Thomasrutter
3
@TJChambers Das stimmt; Wenn Sie das Kennwort für das Konto festlegen können, können Sie sich authentifizieren. Passwort-Hashing soll diesen Angriff nicht verhindern. Es soll verhindern, dass sich ein Angreifer mit schreibgeschütztem Zugriff auf die Kennworttabelle authentifiziert. Beispielsweise erhalten Sie ein Sicherungsband mit der Tabelle darauf.
Erickson
8
@LobsterMan Nein, nicht wirklich. Wenn Sie ein Geheimnis für sich behalten könnten, würden Sie diesen Ansatz nicht verwenden, sondern nur das Passwort speichern. Kennwortauthentifizierungsschemata basieren auf der Annahme, dass der Angreifer alles entdeckt hat, was Sie wissen. Das Salz ist dazu da, dass jedes Passwort einzeln angegriffen werden muss. Der zum Testen von Kennwörtern erforderliche Rechenaufwand wird durch die Iterationen bestimmt. Wenn Benutzer gute Passwörter auswählen, sind sie sicher, auch wenn das Salz aufgedeckt wird. Das Verstecken des Salzes könnte in einigen Fällen jemandem mit einem schlechten Passwort helfen, aber ich würde zuerst an der Passwortqualität arbeiten.
Erickson
1
@NLV Es ist eine Zeichenfolge, die in der bcrypt-Spezifikation definiert ist:"OrpheanBeholderScryDoubt"
erickson
182

Ich glaube, dieser Satz hätte wie folgt formuliert werden müssen:

In bcrypt sind Salze in die generierten Hashes eingebaut , um Regenbogentischangriffe zu verhindern.

Das bcryptDienstprogramm selbst scheint keine Liste von Salzen zu führen. Vielmehr werden Salze zufällig generiert und an die Ausgabe der Funktion angehängt, damit sie später gespeichert werden (gemäß der Java-Implementierung vonbcrypt ). Anders ausgedrückt, der von erzeugte "Hash" bcryptist nicht nur der Hash. Es ist vielmehr der Hasch und das Salz verkettet.

Adam Paynter
quelle
20
OK, also melde ich mich für eine Seite an und wähle das Passwort "foo". Bcryptfügt ein zufälliges Salz von "akd2! *" hinzu, was zu "fooakd2! *" führt, das gehasht und gespeichert wird. Später versuche ich mich mit dem Passwort "bar" anzumelden. Um zu sehen, ob ich richtig bin, muss es "barakd2! *" Hash. Wenn das Salz zunächst zufällig generiert wurde, woher weiß es dann, wie es vor dem Hashing und Vergleichen wieder zu "bar" hinzugefügt werden kann?
Nathan Long
46
@ Nathan: bcryptweiß, wie man das Salz wieder aus der generierten Ausgabe (die in der Datenbank gespeichert ist) extrahiert. Wenn es Zeit für die Authentifizierung ist, bcryptwird die ursprüngliche Ausgabe in ihre Hash- und Salt-Komponenten aufgeteilt. Die Salt-Komponente wird auf das vom Benutzer eingegebene eingehende Kennwort angewendet.
Adam Paynter
22
Um Nathan Longs Kommentar zu beantworten, ist eine gute Denkweise, dass Salze nicht als geheim gedacht sind. Aus diesem Grund ist das Salz in der Ausgabe der bcrypt-Funktion als eine der oben genannten Antworten enthalten. Das Salz soll Regenbogentabellen verhindern, bei denen es sich um Listen gängiger Passwörter oder nur um Brute Force usw. verschiedener Passwörter handelt, die jedoch gehasht wurden. Ohne Salt wäre der Hash für ein Kennwort in Datenbank A der gleiche wie ein Hash für ein Kennwort in Datenbank B. Salt ändert lediglich die Hashwerte, was es für jemanden, der die Datenbank gestohlen hat, schwieriger macht, Kennwörter zu entschlüsseln (zu entschlüsseln).
Joseph Astrahan
11
@ Nathan, aber kann ein Angreifer einfach die bekannten Salze in allen Passwörtern entfernen und dann eine Tabelle mit ihnen erstellen?
Oscar
3
So verstehe ich es: Die Idee ist, dass jedes Passwort ein eindeutiges Salz hat. Das Salz ist im Passwort-Hash enthalten, sodass ein Hacker für jedes Passwort eine Regenbogentabelle erstellen müsste. Dies würde für eine moderate Datenbank enorm viel Zeit in Anspruch nehmen. Es geht darum, einen Angreifer zu verlangsamen und so das brutale Forcen sinnlos zu machen.
PVermeer
0

Um die Dinge noch klarer zu machen,

Registrierungs- / Login-Richtung ->

Das Passwort + Salt wird mit einem Schlüssel verschlüsselt, der aus den Kosten, Salt und dem Passwort generiert wird. Wir nennen diesen verschlüsselten Wert den cipher text. Dann hängen wir das Salt an diesen Wert an und codieren ihn mit base64. Anhängen der Kosten daran und dies ist die produzierte Zeichenfolge aus bcrypt:

$2a$COST$BASE64

Dieser Wert wird schließlich gespeichert.

Was müsste der Angreifer tun, um das Passwort zu finden? (andere Richtung <-)

Falls der Angreifer die Kontrolle über die Datenbank erlangt hat, dekodiert der Angreifer den base64-Wert leicht und kann dann das Salz sehen. Das Salz ist nicht geheim. obwohl es zufällig ist. Dann muss er das entschlüsseln cipher text.

Was wichtiger ist: In diesem Prozess gibt es kein Hashing, sondern eine CPU-teure Verschlüsselung - Entschlüsselung. Daher sind Regenbogentabellen hier weniger relevant.

jony89
quelle
-2

Dies stammt aus der PasswordEncoder-Schnittstellendokumentation von Spring Security.

 * @param rawPassword the raw password to encode and match
 * @param encodedPassword the encoded password from storage to compare with
 * @return true if the raw password, after encoding, matches the encoded password from
 * storage
 */
boolean matches(CharSequence rawPassword, String encodedPassword);

Das heißt, man muss rawPassword abgleichen, das der Benutzer bei der nächsten Anmeldung erneut eingibt, und es mit dem Bcrypt-codierten Passwort abgleichen, das während der vorherigen Anmeldung / Registrierung in der Datenbank gespeichert wurde.

Treffen Sie Shah
quelle
Dies beantwortet die Frage überhaupt nicht ... Es sagt nichts darüber aus, wie bcrypt eingebaute Salze haben kann
spencer.sm