Das Problem hier ist im Grunde ein Problem der Entropie. Beginnen wir also dort:
Entropie pro Zeichen
Die Anzahl der Entropiebits pro Byte beträgt:
- Hex-Zeichen
- Bits: 4
- Werte: 16
- Entropie in 72 Zeichen: 288 Bit
- Alpha-Numerisch
- Bits: 6
- Werte: 62
- Entropie in 72 Zeichen: 432 Bit
- "Allgemeine" Symbole
- Bits: 6.5
- Werte: 94
- Entropie in 72 Zeichen: 468 Bit
- Volle Bytes
- Bits: 8
- Werte: 255
- Entropie in 72 Zeichen: 576 Bit
Wie wir uns verhalten, hängt also davon ab, welche Art von Charakteren wir erwarten.
Das erste Problem
Das erste Problem mit Ihrem Code ist, dass Ihr "Pfeffer" -Hash-Schritt Hex-Zeichen ausgibt (da der vierte Parameter hash_hmac()
nicht gesetzt ist).
Wenn Sie also Ihren Pfeffer einrühren, reduzieren Sie effektiv die maximale Entropie, die für das Passwort verfügbar ist, um den Faktor 2 (von 576 auf 288 mögliche Bits).
Das zweite Problem
Allerdings sha256
bietet nur 256
Bits der Entropie in erster Linie. Sie reduzieren also effektiv mögliche 576 Bit auf 256 Bit. Ihr Hash-Schritt * sofort * verliert per Definition
mindestens 50% der möglichen Entropie im Passwort.
Sie können dies teilweise lösen, indem Sie zu wechseln SHA512
, wo Sie die verfügbare Entropie nur um etwa 12% reduzieren. Aber das ist immer noch ein nicht unerheblicher Unterschied. Diese 12% reduzieren die Anzahl der Permutationen um den Faktor 1.8e19
. Das ist eine große Zahl ... Und das ist der Faktor, der sie reduziert ...
Das zugrunde liegende Problem
Das zugrunde liegende Problem ist, dass es drei Arten von Passwörtern mit mehr als 72 Zeichen gibt. Die Auswirkungen, die dieses Stilsystem auf sie hat, werden sehr unterschiedlich sein:
Hinweis: Von hier an gehe ich davon aus, dass wir mit einem Pfeffersystem vergleichen, das SHA512
mit Rohleistung (nicht hexadezimal) verwendet wird.
Zufällige Passwörter mit hoher Entropie
Dies sind Ihre Benutzer, die Kennwortgeneratoren verwenden, die große Schlüssel für Kennwörter generieren. Sie sind zufällig (erzeugt, nicht vom Menschen ausgewählt) und haben eine hohe Entropie pro Charakter. Diese Typen verwenden High-Bytes (Zeichen> 127) und einige Steuerzeichen.
Für diese Gruppe reduziert Ihre Hashing-Funktion die verfügbare Entropie erheblichbcrypt
.
Lassen Sie mich das noch einmal sagen. Für Benutzer, die lange Passwörter mit hoher Entropie verwenden, reduziert Ihre Lösung die Stärke ihres Passworts erheblich um einen messbaren Betrag. (62 Bit Entropie für ein Kennwort mit 72 Zeichen und mehr für längere Kennwörter verloren)
Zufällige Passwörter mit mittlerer Entropie
Diese Gruppe verwendet Kennwörter, die allgemeine Symbole, aber keine hohen Bytes oder Steuerzeichen enthalten. Dies sind Ihre typisierbaren Passwörter.
Für diese Gruppe werden Sie etwas mehr Entropie freischalten (nicht erstellen, aber mehr Entropie in das bcrypt-Passwort einpassen lassen). Wenn ich etwas sage, meine ich etwas. Die Gewinnschwelle tritt auf, wenn Sie die 512 Bits von SHA512 maximal nutzen. Daher liegt der Peak bei 78 Zeichen.
Lassen Sie mich das noch einmal sagen. Für diese Klasse von Passwörtern können Sie nur weitere 6 Zeichen speichern, bevor Ihnen die Entropie ausgeht.
Nicht zufällige Passwörter mit niedriger Entropie
Dies ist die Gruppe, die alphanumerische Zeichen verwendet, die wahrscheinlich nicht zufällig generiert werden. So etwas wie ein Bibelzitat oder so. Diese Phrasen haben ungefähr 2,3 Bit Entropie pro Zeichen.
Für diese Gruppe können Sie durch Hashing erheblich mehr Entropie freischalten (nicht erstellen, sondern mehr in die Eingabe des bcrypt-Kennworts einfügen). Die Gewinnschwelle liegt bei 223 Zeichen, bevor Ihnen die Entropie ausgeht.
Sagen wir das noch einmal. Für diese Klasse von Passwörtern erhöht das Pre-Hashing die Sicherheit definitiv erheblich.
Zurück in die reale Welt
Diese Art von Entropieberechnungen spielt in der realen Welt keine große Rolle. Was zählt, ist das Erraten der Entropie. Das wirkt sich direkt auf das aus, was Angreifer tun können. Das möchten Sie maximieren.
Während es wenig Forschung gibt, die sich mit dem Erraten von Entropie befasst, gibt es einige Punkte, auf die ich hinweisen möchte.
Die Wahrscheinlichkeit, 72 richtige Zeichen hintereinander zufällig zu erraten, ist äußerst gering. Es ist wahrscheinlicher, dass Sie 21 Mal die Powerball-Lotterie gewinnen, als dass Sie diese Kollision haben ... So groß ist die Zahl, über die wir sprechen.
Aber wir dürfen statistisch nicht darüber stolpern. Bei Phrasen ist die Wahrscheinlichkeit, dass die ersten 72 Zeichen gleich sind, viel höher als bei einem zufälligen Passwort. Aber es ist immer noch trivial niedrig (es ist wahrscheinlicher, dass Sie die Powerball-Lotterie fünfmal gewinnen, basierend auf 2,3 Bit pro Charakter).
Praktisch
Praktisch ist es nicht wirklich wichtig. Die Wahrscheinlichkeit, dass jemand die ersten 72 Zeichen richtig errät, wobei letztere einen signifikanten Unterschied machen, ist so gering, dass es sich nicht lohnt, sich Sorgen zu machen. Warum?
Nehmen wir an, Sie nehmen einen Satz. Wenn die Person die ersten 72 Zeichen richtig machen kann, hat sie entweder wirklich Glück (nicht wahrscheinlich) oder es ist eine übliche Phrase. Wenn es sich um eine gebräuchliche Phrase handelt, ist die einzige Variable, wie lange sie benötigt wird.
Nehmen wir ein Beispiel. Nehmen wir ein Zitat aus der Bibel (nur weil es eine häufige Quelle für Langtext ist, nicht aus irgendeinem anderen Grund):
Du sollst das Haus deines Nachbarn nicht begehren. Du sollst die Frau deines Nachbarn oder seinen Diener oder seine Magd, seinen Ochsen oder Esel oder irgendetwas, was deinem Nachbarn gehört, nicht begehren.
Das sind 180 Zeichen. Das 73. Zeichen ist das g
im zweiten neighbor's
. Wenn Sie so viel erraten haben, werden Sie wahrscheinlich nicht vorbeischauen nei
, sondern mit dem Rest des Verses fortfahren (da das Passwort wahrscheinlich so verwendet wird). Daher hat Ihr "Hash" nicht viel hinzugefügt.
Übrigens: Ich befürworte ABSOLUT NICHT, ein Bibelzitat zu verwenden. In der Tat das genaue Gegenteil.
Fazit
Sie werden Menschen, die lange Passwörter verwenden, nicht wirklich helfen, indem Sie zuerst Hashing durchführen. Bei einigen Gruppen kann man definitiv helfen. Einige kann man definitiv verletzen.
Aber am Ende ist nichts davon übermäßig bedeutsam. Die Zahlen , die wir es zu tun sind nur WAY zu hoch. Der Unterschied in der Entropie wird nicht groß sein.
Sie sollten bcrypt besser so lassen, wie es ist. Es ist wahrscheinlicher, dass Sie das Hashing vermasseln (im wahrsten Sinne des Wortes, Sie haben es bereits getan, und Sie sind nicht der erste oder letzte, der diesen Fehler macht), als dass der Angriff, den Sie verhindern möchten, stattfinden wird.
Konzentrieren Sie sich auf die Sicherung des restlichen Standorts. Fügen Sie bei der Registrierung ein Kennwortentropiemessgerät in das Kennwortfeld ein, um die Kennwortstärke anzuzeigen (und geben Sie an, ob ein Kennwort zu lang ist, das der Benutzer möglicherweise ändern möchte) ...
Das sind mindestens meine $ 0,02 (oder möglicherweise weit mehr als $ 0,02) ...
Soweit die Verwendung eines "geheimen" Pfeffers:
Es gibt buchstäblich keine Forschung darüber, wie man eine Hash-Funktion in bcrypt einspeist. Daher ist es bestenfalls unklar, ob das Einspeisen eines "gepfefferten" Hashs in bcrypt jemals unbekannte Sicherheitslücken verursachen wird (wir wissen, dass dies hash1(hash2($value))
erhebliche Sicherheitslücken in Bezug auf Kollisionsresistenz und Preimage-Angriffe aufdecken kann).
Wenn Sie bereits überlegen, einen geheimen Schlüssel (den "Pfeffer") aufzubewahren, sollten Sie ihn auf eine Weise verwenden, die gut studiert und verstanden ist. Warum nicht den Hash verschlüsseln, bevor er gespeichert wird?
Nachdem Sie das Kennwort gehasht haben, geben Sie die gesamte Hash-Ausgabe in einen starken Verschlüsselungsalgorithmus ein. Speichern Sie dann das verschlüsselte Ergebnis.
Jetzt wird bei einem SQL-Injection-Angriff nichts Nützliches verloren gehen, da sie nicht über den Chiffrierschlüssel verfügen. Und wenn der Schlüssel durchgesickert ist, sind die Angreifer nicht besser dran als wenn Sie einen einfachen Hash verwendet haben (was nachweislich etwas mit dem Pfeffer "Pre-Hash" nicht bietet).
Hinweis: Wenn Sie sich dazu entscheiden, verwenden Sie eine Bibliothek. Für PHP empfehle ich dringend das Zend\Crypt
Paket von Zend Framework 2 . Es ist tatsächlich das einzige, das ich zu diesem Zeitpunkt empfehlen würde. Es wurde stark überprüft und trifft alle Entscheidungen für Sie (was eine sehr gute Sache ist) ...
Etwas wie:
use Zend\Crypt\BlockCipher;
public function createHash($password) {
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
return $blockCipher->encrypt($hash);
}
public function verifyHash($password, $hash) {
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
$hash = $blockCipher->decrypt($hash);
return password_verify($password, $hash);
}
Und es ist vorteilhaft, weil Sie alle Algorithmen auf eine Weise verwenden, die gut verstanden und gut untersucht ist (zumindest relativ). Merken:
Jeder, vom ahnungslosesten Amateur bis zum besten Kryptographen, kann einen Algorithmus erstellen, den er selbst nicht brechen kann.