Nun, Sie könnten es nachschlagen in Wikipedia ... Aber da Sie eine Erklärung wollen, werde ich mein Bestes tun:
Hash-Funktionen
Sie bieten eine Zuordnung zwischen einer Eingabe beliebiger Länge und einer (normalerweise) festen Länge (oder kleineren Länge) der Ausgabe. Das kann alles sein, von einem einfachen crc32 bis hin zu einer vollwertigen kryptographischen Hash-Funktion wie MD5 oder SHA1/2/256/512. Der Punkt ist, dass es sich um eine einseitige Zuordnung handelt. Es ist immer eine viele:1-Zuordnung (d.h. es wird immer Kollisionen geben), da jede Funktion eine kleinere Ausgabe produziert, als sie eingeben kann (wenn man jede mögliche 1mb-Datei in MD5 einspeist, wird man eine Menge Kollisionen bekommen).
Der Grund, warum sie nur schwer (oder praktisch gar nicht) rückgängig zu machen sind, liegt in ihrer internen Funktionsweise. Die meisten kryptografischen Hash-Funktionen durchlaufen den Eingabesatz viele Male, um die Ausgabe zu erzeugen. Wenn wir uns also jedes Stück der Eingabe mit fester Länge ansehen (was vom Algorithmus abhängt), bezeichnet die Hash-Funktion dies als den aktuellen Zustand. Dann durchläuft sie diesen Zustand, ändert ihn in einen neuen und verwendet ihn als Rückmeldung für sich selbst (MD5 tut dies 64 Mal für jedes 512-Bit-Datenpaket). Anschließend werden die sich aus all diesen Iterationen ergebenden Zustände irgendwie miteinander kombiniert, um den resultierenden Hashwert zu bilden.
Wollte man nun den Hash entschlüsseln, müsste man zunächst herausfinden, wie man den gegebenen Hash in seine iterierten Zustände aufteilt (1 Möglichkeit für Eingaben, die kleiner sind als die Größe eines Datenpakets, viele für größere Eingaben). Dann müsste man die Iteration für jeden Zustand umkehren. Um zu erklären, warum das SEHR schwierig ist, stellen Sie sich vor, dass Sie versuchen, Folgendes abzuleiten a
y b
nach der folgenden Formel: 10 = a + b
. Es gibt 10 positive Kombinationen von a
y b
die funktionieren können. Jetzt wiederholen Sie das ein paar Mal: tmp = a + b; a = b; b = tmp
. Bei 64 Iterationen hätte man über 10^64 Möglichkeiten zum Ausprobieren. Und das ist nur eine einfache Addition, bei der ein gewisser Zustand von Iteration zu Iteration erhalten bleibt. Echte Hash-Funktionen führen viel mehr als eine Operation durch (MD5 führt etwa 15 Operationen an 4 Zustandsvariablen durch). Und da die nächste Iteration vom Zustand der vorherigen abhängt und der vorherige Zustand bei der Erzeugung des aktuellen Zustands zerstört wird, ist es nahezu unmöglich, den Eingangszustand zu bestimmen, der zu einem bestimmten Ausgangszustand geführt hat (und das bei jeder Iteration). Kombiniert man dies mit der großen Anzahl von Möglichkeiten, die damit verbunden sind, so erfordert die Dekodierung selbst eines MD5 eine nahezu unendliche (aber nicht unendliche) Menge an Ressourcen. So viele Ressourcen, dass es deutlich billiger ist, den Hash zu erzwingen, wenn man eine Vorstellung von der Größe der Eingabe hat (bei kleineren Eingaben), als zu versuchen, den Hash zu entschlüsseln.
Verschlüsselungsfunktionen
Sie bieten eine 1:1-Abbildung zwischen einer Eingabe und einer Ausgabe beliebiger Länge. Und sie sind immer umkehrbar. Wichtig ist, dass sie mit einer bestimmten Methode umkehrbar sind. Und es ist immer 1:1 für einen bestimmten Schlüssel. Nun gibt es mehrere Eingabe-Schlüssel-Paare, die dieselbe Ausgabe erzeugen können (je nach Verschlüsselungsfunktion gibt es das in der Regel). Gute verschlüsselte Daten sind von zufälligem Rauschen nicht zu unterscheiden. Dies unterscheidet sich von einer guten Hash-Ausgabe, die immer ein einheitliches Format hat.
Anwendungsfälle
Verwenden Sie eine Hash-Funktion, wenn Sie einen Wert vergleichen wollen, aber die einfache Darstellung nicht speichern können (aus einer Vielzahl von Gründen). Passwörter eignen sich sehr gut für diesen Anwendungsfall, da man sie aus Sicherheitsgründen nicht im Klartext speichern möchte (und sollte). Was aber, wenn man ein Dateisystem auf raubkopierte Musikdateien überprüfen möchte? 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 Byte statt 3 MB speichern). Auf diese Weise kann man einfach jede Datei mit einem Hash versehen und mit der gespeicherten Datenbank von Hashes vergleichen (in der Praxis funktioniert das nicht so gut, weil die Dateien neu codiert werden, die Dateiköpfe geändert werden usw., aber es ist ein Beispiel für einen Anwendungsfall).
Verwenden Sie eine Hash-Funktion, wenn Sie die Gültigkeit von Eingabedaten überprüfen. Dafür sind sie ja gedacht. Wenn Sie 2 Eingabedaten haben und prüfen wollen, ob sie gleich sind, lassen Sie beide durch eine Hash-Funktion laufen. Die Wahrscheinlichkeit einer Kollision ist bei kleinen Eingabedaten astronomisch gering (eine gute Hash-Funktion vorausgesetzt). Deshalb wird sie für Passwörter empfohlen. Für Passwörter mit bis zu 32 Zeichen hat md5 den 4-fachen Ausgabespeicher. SHA1 hat den 6-fachen Ausgaberaum (ungefähr). SHA512 hat etwa den 16-fachen Ausgabespeicherplatz. Es ist eigentlich egal, wie das Kennwort lautet. war Sie interessiert, ob es die gleiche ist wie die, die gespeichert wurde. Deshalb sollten Sie Hashes für Passwörter verwenden.
Verwenden Sie die Verschlüsselung immer dann, wenn Sie die Eingabedaten wieder herausbekommen müssen. Beachten Sie das Wort brauchen . Wenn Sie Kreditkartennummern speichern, müssen Sie sie irgendwann wieder herausholen, wollen 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 z. B. HMAC verwenden, signieren Sie einen Teil der Daten, indem Sie einen Hash-Wert der Daten mit einem bekannten, aber nicht übertragenen Wert (einem geheimen Wert) verknüpfen. Sie senden also den Klartext und den HMAC-Hash. Der Empfänger verschlüsselt dann einfach die übermittelten Daten mit dem bekannten Wert und prüft, ob er mit dem übermittelten HMAC übereinstimmt. Wenn dies der Fall ist, wissen Sie, dass die Daten nicht von einer Partei manipuliert wurden, die den geheimen Wert nicht kennt. Dies wird häufig in sicheren Cookie-Systemen von HTTP-Frameworks sowie bei der Übertragung von Daten über HTTP verwendet, wenn eine gewisse Integrität der Daten gewährleistet werden soll.
Ein Hinweis zu Hashes für Kennwörter:
Ein wesentliches Merkmal kryptographischer Hash-Funktionen ist, dass sie sehr schnell zu erstellen sind und sehr schwer/langsam rückgängig zu machen (so sehr, dass es praktisch unmöglich ist). Dies stellt ein Problem bei Passwörtern dar. Wenn Sie Folgendes speichern sha512(password)
tun Sie nichts, um sich gegen Rainbow Tables oder Brute-Force-Angriffe 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 durch die Hash-Funktion laufen zu lassen und jedes Ergebnis zu testen.
Das Hinzufügen eines Salzes ist hilfreich, da es dem Hash ein paar unbekannte Daten hinzufügt. Anstatt also etwas zu finden, das mit md5(foo)
Sie müssen etwas finden, das in Verbindung mit dem bekannten Salz md5(foo.salt)
(was sehr viel schwieriger zu bewerkstelligen ist). Das Geschwindigkeitsproblem ist damit aber immer noch nicht gelöst, denn wenn sie das Salz kennen, müssen sie nur das Wörterbuch durchlaufen lassen.
Es gibt also Möglichkeiten, damit umzugehen. Eine beliebte Methode ist die wesentliche Stärkung (oder Schlüsseldehnung). Im Grunde genommen wird ein Hash viele Male durchlaufen (in der Regel Tausende). Dies bewirkt zwei Dinge. Erstens verlangsamt es die Laufzeit des Hash-Algorithmus erheblich. Zweitens erhöht es, wenn es richtig implementiert wird (indem Eingabe und Salz bei jeder Iteration erneut eingegeben werden), die Entropie (den verfügbaren 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, eher standardisierte Implementierungen wie PBKDF2 , BCrypt . Diese Technik wird jedoch von vielen sicherheitsrelevanten Systemen verwendet (z. B. PGP, WPA, Apache und OpenSSL).
Die Quintessenz, 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 erzeugen...
Ein weiterer Hinweis zur trivialen Streckung
Die Ausgabe einer Hash-Funktion darf unter keinen Umständen direkt in die Hash-Funktion zurückgeführt werden. :
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash); // <-- Do NOT do this!
}
Der Grund dafür liegt in den Kollisionen. Bedenken Sie, dass es bei allen Hash-Funktionen zu Kollisionen kommt, weil der mögliche Ausgaberaum (die Anzahl der möglichen Ausgaben) kleiner ist als der Eingaberaum. Um zu sehen, warum das so ist, sehen wir uns an, was passiert. Gehen wir zunächst davon aus, dass die Wahrscheinlichkeit einer Kollision von 0,001 % besteht. sha1()
(es ist viel niedriger als in der Realität, aber zu Demonstrationszwecken).
hash1 = sha1(password + salt);
Jetzt, hash1
hat eine Kollisionswahrscheinlichkeit von 0,001 %. Aber wenn wir die nächste hash2 = sha1(hash1);
, alle Kollisionen von hash1
werden automatisch zu Kollisionen von hash2
. Damit liegt die Rate von Hash1 bei 0,001 %, und die 2. sha1()
Anruf trägt dazu bei. Also jetzt, hash2
hat eine Kollisionswahrscheinlichkeit von 0,002 %. Das sind doppelt so viele Chancen! Jede Iteration fügt eine weitere 0.001%
Chance einer Kollision mit dem Ergebnis. Bei 1000 Iterationen stieg die Kollisionswahrscheinlichkeit von trivialen 0,001 % auf 1 %. Jetzt ist die Verschlechterung linear, und die tatsächlichen Wahrscheinlichkeiten sind weit kleiner, aber der Effekt ist derselbe (eine Schätzung der Wahrscheinlichkeit eines einzelnen Zusammenstoßes mit md5
beträgt etwa 1/(2 128 ) oder 1/(3x10 38 ). Das scheint zwar wenig, aber dank der der Geburtstagsanschlag es ist nicht so klein, wie es scheint).
Stattdessen werden durch die erneute Eingabe von Salt und Kennwort jedes Mal erneut Daten in die Hash-Funktion eingebracht. So sind Kollisionen in einer bestimmten Runde keine Kollisionen mehr in der nächsten Runde. So:
hash = sha512(password + salt);
for (i = 0; i < 1000; i++) {
hash = sha512(hash + password + salt);
}
Hat die gleiche Kollisionswahrscheinlichkeit wie der Eingeborene sha512
Funktion. Das ist es, was Sie wollen. Verwenden Sie das stattdessen.
31 Stimmen
Ich kann mir vorstellen, dass dies die Frage, auf die sich die Leute beziehen können, wenn sie die Begriffe verwechseln :)
20 Stimmen
Hashing ist einseitig (kann nicht rückgängig gemacht werden), Verschlüsselung ist zweiseitig (kann entschlüsselt werden)
1 Stimmen
Hashes sind auch nützlich für die Indizierung großer Strukturen und Objekte, z. B. Dateien. Siehe Hash-Tabelle .
25 Stimmen
Hashing ist wie ein Fleischwolf. Man kann eine Kuh in einen Hamburger verwandeln, aber nicht umgekehrt.
0 Stimmen
Ich habe bemerkt, dass meine Frage bearbeitet wurde. Ich kannte schon immer die Unterschiede auf der obersten Ebene zwischen den beiden, war aber eher neugierig auf die Unterschiede auf der unteren/mathematischen Ebene :) Wie auch immer, viel guter Inhalt für SO! Vielen Dank dafür!