
Die hmac-bcrypt-Passwort-Hashfunktion
Dieses Repository enthält Referenzimplementierungen der Passwort-Hashfunktion hmac-bcrypt in mehreren Sprachen. Jede Referenzimplementierung versucht, wo möglich eine 1:1-Portierung der ursprünglichen C- und Perl-Implementierungen von @epixoip zu sein, und ist vollständig kompatibel mit den anderen (d. h. sie erzeugen und validieren dieselben Hashwerte).
Jede Referenzimplementierung definiert zwei prozedurale Funktionen mit den folgenden Pseudo-Prototypen:
string hmac_bcrypt_hash(password, settings?, pepper?)
boolean hmac_bcrypt_verify(password, expected, pepper?)
Bitte beachten Sie die mit jeder Referenzimplementierung bereitgestellten Testfälle, um zu erfahren, wie diese Funktionen in Ihr Projekt integriert und verwendet werden. Aus Einfachheitsgründen wurde eine prozedurale Schnittstelle gewählt, aber Sie können diese Funktionen gerne in Klassen oder Objekte einbinden, wie Sie möchten.
Der Parameter settings bezieht sich in diesem Kontext auf einen Standard-bcrypt-Settings-String, der den Hash-Identifikator (2a), den log2-Kostenwert (z. B. 13) und optional einen 22-Byte-, radix64-codierten Salt-Wert (z. B. LhayLxezLhK1LhWvKxCyLO) enthält. Diese Werte werden zu einer durch Dollarzeichen getrennten Zeichenkette zusammengefügt; z. B. $2a$13$LhayLxezLhK1LhWvKxCyLO.
Der Parameter settings ist optional; in den meisten Fällen sollte er null/leer gelassen werden, um den Standardkostenwert 13 und einen generierten Salt zu verwenden. Höchstens, wenn Sie einen anderen Kostenwert als 13 verwenden möchten, können Sie nur die ID + den Kostenwert angeben (z. B. $2a$10$). Es wird nicht empfohlen, eigene Salt-Werte zu erzeugen und anzugeben!
Der Parameter pepper definiert ein globales gemeinsames Geheimnis und ist ebenfalls optional; wenn er null/leer ist, wird der Standardwert von hmac_bcrypt verwendet. Dies dient in erster Linie der Abwehr von Shucking-Angriffen, kann aber auch verwendet werden, um die Sicherheit, den Aufwand und die Kosten für das Knacken zu erhöhen (insbesondere in Verbindung mit einem HSM).
Die Passwort-Hashfunktion hmac-bcrypt verwendet bcrypt mit ordnungsgemäßem Pre-Hashing und Post-Hashing, kombiniert mit einem optionalen Pepper. In Pseudocode ist das ziemlich einfach:
pre_hash = hmac_sha512_base64(password, pepper)
mid_hash = bcrypt(pre_hash, settings)
post_hash = hmac_sha512_base64(mid_hash, pepper)
return settings + post_hash
Pre-Hashing wird eingesetzt, um Eingabelängen zu ermöglichen, die größer sind als das bcrypt-Maximum von 72 Eingabebytes. SHA-512 wurde aufgrund seiner 64-Bit-Wortgröße gewählt, die für CPU-Verteidiger freundlich ist, aber GPU-Angreifer behindert. Ein roher SHA-512-Wert kann jedoch aus mehreren Gründen nicht verwendet werden:
Um Shucking-Angriffe abzumildern, muss der Pre-Hash gesalzen – oder in diesem Fall mit Pepper versehen – werden, und HMAC bietet ein praktisches Mittel zur Schlüsselung eines Hashs. Der resultierende HMAC-Wert wird dann mit base64 codiert, um eine saubere Eingabe im unteren ASCII-Bereich zu erzeugen, die Probleme mit Nullbytes und binären Daten abmildert.
Der aufmerksame Leser wird feststellen, dass hmac_sha512_base64 88 Bytes Daten erzeugt, während bcrypt eine maximale Eingabegröße von 72 Bytes hat. Das ist kein Problem und sogar bevorzugt gegenüber der Verwendung eines Hash-Algorithmus, der weniger Eingabedaten erzeugt, wie z. B. sha256. Wir möchten alle 72 Bytes füllen, und es geht keine Sicherheit verloren, wenn sha512 auf 432 Bits gekürzt wird (dies ist mehr als die 384 Bits, die sha384 bietet).
Post-Hashing wird hauptsächlich eingesetzt, um hmac-bcrypt-Hashes von bcrypt-Hashes zu unterscheiden – d. h. die Längen unterscheiden sich –, aber auch, um dank des Peppers eine zusätzliche Schutzschicht hinzuzufügen. Der Post-Hashing-Schritt könnte sogar mit dem in einem HSM gespeicherten Pepper-Wert durchgeführt werden (sehr empfehlenswert!) für weiteren Schutz.
Während Speicherhärte ein interessantes Experiment war, ist der richtige Weg zur Erzielung von Beschleunigungsresistenz ganz klar Cache-Härte. Speichergeschwindigkeiten und -bandbreite nehmen weiter zu, während RAM größer, billiger und dichter wird. Aber Cache-Größen, Cache-Geschwindigkeiten und Cache-Kosten sind relativ statisch. Selbst Hardware-Scatter/Gather-Befehle hatten nicht die dramatischen Auswirkungen, die wir einst für cache-harte Algorithmen vorhergesagt hatten.
Die besten speicherharten Algorithmen – Argon2 und scrypt – sind bei einer Ziel-Laufzeit von weniger als 1000 ms tatsächlich weniger beschleunigungsresistent als cache-harte Algorithmen, was sie zu einem großartigen KDF, aber nicht großartig für die Echtzeit-Authentifizierung macht.
Idealerweise würde man eine absichtlich cache-harte Passwort-Hashfunktion wie pufferfish oder bscrypt verwenden. Diese Funktionen sind jedoch neuer, weniger erforscht und bieten nur wenige Bibliotheken. bcrypt hingegen – wenn auch unbeabsichtigt cache-hart – ist praktisch für jede Sprache und jedes Framework verfügbar. Von den Algorithmen, die uns ohne Weiteres zur Verfügung stehen, bietet bcrypt den größten Widerstand gegen Beschleunigung für die Echtzeit-, interaktive Authentifizierung (Ziel-Laufzeit < 1000 ms), also ist die offensichtliche Antwort, das bcrypt zu nutzen, das uns zur Verfügung steht.
Allerdings hat bcrypt einige bemerkenswerte Einschränkungen, wie seine sehr lautstarken Kritiker schnell betonen:
hmac-bcrypt behebt beide dieser Probleme und noch mehr.