Nun, Sie könnten es in Wikipedia nachschlagen ... Aber da Sie eine Erklärung wünschen, werde ich hier mein Bestes geben:
Hash-Funktionen
Sie bieten eine Zuordnung zwischen einer Eingabe beliebiger Länge und einer Ausgabe (normalerweise) fester Länge (oder kleinerer Länge). Dies kann alles sein, von einem einfachen crc32 bis zu einer vollständigen kryptografischen Hash-Funktion wie MD5 oder SHA1 / 2/256/512. Der Punkt ist, dass es eine Einweg-Zuordnung gibt. Es ist immer eine Zuordnung von vielen: 1 (was bedeutet, dass es immer Kollisionen gibt), da jede Funktion eine kleinere Ausgabe erzeugt, als sie eingeben kann (Wenn Sie jede mögliche 1-MB-Datei in MD5 einspeisen, erhalten Sie eine Menge Kollisionen).
Der Grund, warum sie schwer (oder praktisch unmöglich) umzukehren sind, liegt darin, wie sie intern arbeiten. Die meisten kryptografischen Hash-Funktionen durchlaufen den Eingabesatz viele Male, um die Ausgabe zu erzeugen. Wenn wir uns also jeden Teil der Eingabe mit fester Länge ansehen (der vom Algorithmus abhängig ist), ruft die Hash-Funktion diesen aktuellen Status auf. Es wird dann den Status durchlaufen und in einen neuen ändern und diesen als Rückmeldung in sich selbst verwenden (MD5 führt dies 64 Mal für jeden 512-Bit-Datenblock durch). Es kombiniert dann irgendwie die resultierenden Zustände aus all diesen Iterationen wieder zusammen, um den resultierenden Hash zu bilden.
Wenn Sie nun den Hash dekodieren möchten, müssen Sie zunächst herausfinden, wie der angegebene Hash in seine iterierten Zustände aufgeteilt werden kann (1 Möglichkeit für Eingaben, die kleiner als die Größe eines Datenblocks sind, viele für größere Eingaben). Dann müssten Sie die Iteration für jeden Zustand umkehren. Nun, zu erklären , warum dies ist sehr schwer, sich vorstellen , versuchen abzuleiten a
und b
aus der folgenden Formel: 10 = a + b
. Es gibt 10 positive Kombinationen von a
und b
das kann funktionieren. Führen Sie dies nun einige Male durch:tmp = a + b; a = b; b = tmp
. Für 64 Iterationen hätten Sie über 10 ^ 64 Möglichkeiten, es zu versuchen. Und das ist nur eine einfache Ergänzung, bei der ein Zustand von Iteration zu Iteration erhalten bleibt. Echte Hash-Funktionen führen viel mehr als eine Operation aus (MD5 führt ungefähr 15 Operationen mit 4 Zustandsvariablen aus). Und da die nächste Iteration vom Status der vorherigen abhängt und die vorherige beim Erstellen des aktuellen Status zerstört wird, ist es so gut wie unmöglich, den Eingabestatus zu bestimmen, der zu einem bestimmten Ausgabezustand geführt hat (für jede Iteration nicht weniger). Kombinieren Sie dies mit der großen Anzahl von Möglichkeiten, und das Decodieren selbst eines MD5 wird eine nahezu unendliche (aber nicht unendliche) Menge an Ressourcen erfordern. So viele Ressourcen, dass es '
Verschlüsselungsfunktionen
Sie bieten eine 1: 1-Zuordnung zwischen einer Eingabe und Ausgabe beliebiger Länge. Und sie sind immer reversibel. Das Wichtigste ist, dass es mit einer Methode reversibel ist. Und für einen bestimmten Schlüssel ist es immer 1: 1. Jetzt gibt es mehrere Eingabe: Schlüsselpaare, die möglicherweise dieselbe Ausgabe erzeugen (tatsächlich gibt es diese normalerweise, abhängig von der Verschlüsselungsfunktion). Gute verschlüsselte Daten sind nicht von zufälligem Rauschen zu unterscheiden. Dies unterscheidet sich von einer guten Hash-Ausgabe, die immer ein konsistentes Format hat.
Anwendungsfälle
Verwenden Sie eine Hash-Funktion, wenn Sie einen Wert vergleichen möchten, die einfache Darstellung jedoch nicht speichern können (aus einer Reihe von Gründen). Passwörter sollten sehr gut zu diesem Anwendungsfall passen, da Sie sie aus Sicherheitsgründen nicht im Klartext speichern möchten (und sollten). Aber was ist, wenn Sie ein Dateisystem auf Raubkopien von Musikdateien überprüfen möchten? Es wäre unpraktisch, 3 MB pro Musikdatei zu speichern. Nehmen Sie stattdessen den Hash der Datei und speichern Sie diesen (md5 würde 16 Bytes anstelle von 3 MB speichern). Auf diese Weise müssen Sie nur jede Datei hashen und mit der gespeicherten Datenbank mit Hashes vergleichen (dies funktioniert in der Praxis aufgrund von Neucodierung, Ändern von Dateikopfzeilen usw. nicht so gut, ist jedoch ein Beispiel für einen Anwendungsfall).
Verwenden Sie eine Hash-Funktion, wenn Sie die Gültigkeit der Eingabedaten überprüfen. Dafür sind sie konzipiert. Wenn Sie zwei Eingabeteile haben und überprüfen möchten, ob diese identisch sind, führen Sie beide über eine Hash-Funktion aus. Die Wahrscheinlichkeit einer Kollision ist für kleine Eingabegrößen astronomisch gering (unter der Annahme einer guten Hash-Funktion). Deshalb wird es für Passwörter empfohlen. Für Passwörter mit bis zu 32 Zeichen hat md5 das 4-fache des Ausgaberaums. SHA1 hat das 6-fache des Ausgaberaums (ungefähr). SHA512 hat ungefähr das 16-fache des Ausgaberaums. Es ist dir egal, wie das Passwort lautete , es ist dir egal, ob es das gleiche ist wie das, das gespeichert wurde. Deshalb sollten Sie Hashes für Passwörter verwenden.
Verwenden Sie die Verschlüsselung immer dann, wenn Sie die Eingabedaten wieder herausholen möchten. Beachten Sie das Wort brauchen . Wenn Sie Kreditkartennummern speichern, müssen Sie diese irgendwann wieder herausholen, möchten sie aber nicht im Klartext speichern. Speichern Sie stattdessen die verschlüsselte Version und bewahren Sie den Schlüssel so sicher wie möglich auf.
Hash-Funktionen eignen sich auch hervorragend zum Signieren von Daten. Wenn Sie beispielsweise HMAC verwenden, signieren Sie ein Datenelement, indem Sie einen Hash der Daten verwenden, die mit einem bekannten, aber nicht übertragenen Wert (einem geheimen Wert) verknüpft sind. Sie senden also den Klartext und den HMAC-Hash. Dann hasht der Empfänger einfach die gesendeten Daten mit dem bekannten Wert und prüft, ob sie mit dem übertragenen HMAC übereinstimmen. Wenn es dasselbe ist, wissen Sie, dass es nicht von einer Partei ohne den geheimen Wert manipuliert wurde. Dies wird häufig in sicheren Cookie-Systemen von HTTP-Frameworks sowie bei der Nachrichtenübertragung von Daten über HTTP verwendet, wenn Sie die Integrität der Daten sicherstellen möchten.
Ein Hinweis zu Hashes für Passwörter:
Ein wesentliches Merkmal kryptografischer Hash-Funktionen ist, dass sie sehr schnell zu erstellen und sehr schwer / langsam umzukehren sind (so sehr, dass dies praktisch unmöglich ist). Dies wirft ein Problem mit Passwörtern auf. Wenn Sie speichern sha512(password)
, tun Sie nichts, um sich vor Regenbogentischen oder Brute-Force-Angriffen zu schützen. Denken Sie daran, dass die Hash-Funktion auf Geschwindigkeit ausgelegt ist. Daher ist es für einen Angreifer trivial, einfach ein Wörterbuch über die Hash-Funktion auszuführen und jedes Ergebnis zu testen.
Das Hinzufügen eines Salzes hilft, da es dem Hash ein paar unbekannte Daten hinzufügt. Anstatt also etwas zu finden, das passt md5(foo)
, müssen sie etwas finden, das, wenn es dem bekannten Salz zugesetzt wird, produziert md5(foo.salt)
(was sehr viel schwieriger ist). Aber es löst das Geschwindigkeitsproblem immer noch nicht, denn wenn sie das Salz kennen, ist es nur eine Frage des Durchlaufens des Wörterbuchs.
Es gibt also Möglichkeiten, damit umzugehen. Eine beliebte Methode ist die Schlüsselverstärkung (oder Schlüsseldehnung). Grundsätzlich iterieren Sie viele Male über einen Hash (normalerweise Tausende). Dies macht zwei Dinge. Erstens verlangsamt es die Laufzeit des Hashing-Algorithmus erheblich. Zweitens erhöht eine korrekte Implementierung (Weitergabe der Eingabe und des Salzes bei jeder Iteration) tatsächlich die Entropie (verfügbarer Speicherplatz) für die Ausgabe, wodurch die Wahrscheinlichkeit von Kollisionen verringert wird. Eine triviale Implementierung ist:
var hash = password + salt;
for (var i = 0; i < 5000; i++) {
hash = sha512(hash + password + salt);
}
Es gibt andere Standardimplementierungen wie PBKDF2 , BCrypt . Diese Technik wird jedoch von einigen sicherheitsrelevanten Systemen (wie PGP, WPA, Apache und OpenSSL) verwendet.
Das Endergebnis hash(password)
ist nicht gut genug. hash(password + salt)
ist besser, aber immer noch nicht gut genug ... Verwenden Sie einen gestreckten Hash-Mechanismus, um Ihre Passwort-Hashes zu erstellen ...
Ein weiterer Hinweis zum trivialen Dehnen
Führen Sie die Ausgabe eines Hashs unter keinen Umständen direkt in die Hash-Funktion zurück :
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash); // <-- Do NOT do this!
}
Der Grund dafür liegt in Kollisionen. Denken Sie daran, dass alle Hash-Funktionen Kollisionen aufweisen, da der mögliche Ausgaberaum (die Anzahl der möglichen Ausgaben) kleiner als der Eingaberaum ist. Um zu sehen warum, schauen wir uns an, was passiert. Nehmen wir vorab an, dass die Wahrscheinlichkeit einer Kollision bei 0,001% liegt sha1()
( in der Realität ist sie viel geringer, aber zu Demonstrationszwecken).
hash1 = sha1(password + salt);
Hat jetzt hash1
eine Kollisionswahrscheinlichkeit von 0,001%. Aber wenn wir das nächste Mal machen hash2 = sha1(hash1);
, werden alle Kollisionen von hash1
automatisch zu Kollisionen vonhash2
. Jetzt haben wir eine Hash1-Rate von 0,001%, und der zweite sha1()
Aufruf trägt dazu bei. Hat jetzt hash2
also eine Kollisionswahrscheinlichkeit von 0,002%. Das sind doppelt so viele Chancen! Jede Iteration fügt 0.001%
dem Ergebnis eine weitere Kollisionswahrscheinlichkeit hinzu. Bei 1000 Iterationen stieg die Kollisionswahrscheinlichkeit von trivialen 0,001% auf 1%. Jetzt ist die Verschlechterung linear und die realen Wahrscheinlichkeiten sind weitaus geringer, aber der Effekt ist der gleiche (eine Schätzung der Wahrscheinlichkeit einer einzelnen Kollision mit md5
beträgt ungefähr 1 / (2 128 ) oder 1 / (3x10 38)). Das scheint zwar klein zu sein, aber dank des Geburtstagsangriffs ist es nicht wirklich so klein, wie es scheint.
Stattdessen führen Sie durch erneutes Anhängen von Salt und Kennwort jedes Mal Daten wieder in die Hash-Funktion ein. Kollisionen einer bestimmten Runde sind also keine Kollisionen der nächsten Runde mehr. Damit:
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash + password + salt);
}
Hat die gleiche Kollisionswahrscheinlichkeit wie die native sha512
Funktion. Welches ist was du willst. Verwenden Sie das stattdessen.