Hashing vs. Verschlüsselung vs. Kodierung: Was oft verwechselt wird
Kodierung, Verschlüsselung und Hashing werden ständig durcheinandergebracht, und die Fehler brechen echten Auth-Code. Hier ist der Unterschied und wann man was verwendet.
Ich habe viel Authentifizierungscode reviewed, und derselbe Kategoriefehler taucht immer wieder auf. Jemand Base64-kodiert ein Passwort und nennt es “verschlüsselt”. Jemand hasht eine Kreditkartennummer, obwohl er sie später entschlüsseln muss. Jemand greift zu SHA-256 für die Passwortspeicherung, weil es sicher klingt. Das sind keine kleinen Fehler. Das ist der Unterschied zwischen einem System, das Nutzer schützt, und einem, das beim ersten Datenbankdump alles preisgibt. Die Lösung beginnt damit zu wissen, dass Kodierung, Verschlüsselung und Hashing drei separate Dinge sind, die drei separate Probleme lösen.
Die Ein-Satz-Version von jedem
Vor den Details, das Ganze verdichtet:
- Kodierung ändert das Format von Daten, damit ein anderes System sie lesen kann. Umkehrbar, kein Schlüssel, kein Sicherheitswert.
- Verschlüsselung verschlüsselt Daten, damit nur jemand mit dem Schlüssel sie lesen kann. Mit dem Schlüssel umkehrbar, gibt Vertraulichkeit.
- Hashing wandelt Daten in einen Fingerabdruck fester Größe um. Einwegig, nicht umkehrbar, wird für Integrität und Verifikation verwendet.
Die Falle ist, dass alle drei Ausgaben erzeugen, die wie zufälligen Zeichensalat aussehen, also werden sie als austauschbar behandelt. Das sind sie nicht. Die Frage, die sie unterscheidet, ist einfach: muss man die Originaldaten je zurückhaben, und wenn ja, wer darf sie bekommen?
Kodierung: Format, nicht Schutz
Kodierung existiert, weil Daten durch Systeme reisen müssen, die nur bestimmte Bytebereiche akzeptieren. E-Mail-Körper, URLs und JSON-Payloads haben alle Zeichen, die sie nicht sicher übertragen können, also werden die rohen Bytes in einem freundlicheren Alphabet ausgedrückt. Base64 bildet beliebige Bytes auf 64 druckbare Zeichen ab. Hex bildet jedes Byte auf zwei Zeichen in 0-9a-f ab. URL-Kodierung verwandelt ein Leerzeichen in %20, damit es innerhalb eines Query-Strings überlebt.
Das Wichtige zu verinnerlichen: Kodierung hat keinen Schlüssel und kein Geheimnis. Jeder, der den kodierten String hält, kann das Original sofort wiederherstellen. Es gibt nichts zu knacken, weil nichts verschlossen wurde.
echo "aGVsbG8=" | base64 -d
hello
Das ist der gesamte “Angriff.” Ein Befehl, kein Schlüssel, fertig. Man kann es mit dem Base64 Encode/Decode -Tool beweisen, indem man einen beliebigen kodierten String einfügt und beobachtet, wie er direkt zurückkommt.
Kodierung ist nützlich. So werden Binärdateien in JSON eingebettet, so funktionieren Data-URIs, so werden Basic-Auth-Header formatiert. Das Problem ist rein das Missverständnis. Ich habe Authorization: Basic <base64>-Header gesehen, die in Design-Dokumenten als “verschlüsselte Anmeldedaten” beschrieben wurden. Sie sind überhaupt nicht verschlüsselt. Basic-Auth-Base64 ist für jeden, der die Anfrage sieht, umkehrbar, weswegen es ausschließlich über TLS gesendet werden darf.
Wenn man eine Regel aus diesem Abschnitt mitnimmt: Kodierung ist niemals eine Sicherheitsmaßnahme. Wenn das Bedrohungsmodell jemanden einschließt, der die Daten nicht lesen soll, tut Kodierung nichts dafür.
Verschlüsselung: umkehrbar, aber nur mit dem Schlüssel
Verschlüsselung ist der Punkt, wo tatsächlich Geheimnisse ins Spiel kommen. Man nimmt Klartext, kombiniert ihn mit einem Schlüssel und produziert Geheimtext, der ohne diesen Schlüssel nutzlos ist. Der ganze Punkt ist, dass die Transformation umkehrbar ist, aber nur durch die Partei, die den richtigen Schlüssel hält. Diese Eigenschaft nennt sich Vertraulichkeit.
Man verwendet Verschlüsselung, wenn man die Originaldaten später zurückhaben muss. Das Speichern eines OAuth-Refresh-Tokens eines Nutzers, das Verschlüsseln einer Datei vor dem Hochladen in Speicher, dem man nicht vollständig vertraut, das Schützen eines Session-Payloads in einem Cookie: all das braucht den Klartext irgendwann, also braucht man Verschlüsselung, nicht Hashing.
Moderne symmetrische Verschlüsselung bedeutet AES, fast immer AES-256 in einem authentifizierten Modus wie GCM. “Authentifiziert” ist wichtig: AES-256-GCM gibt sowohl Vertraulichkeit als auch einen eingebauten Integritätstag, sodass wenn jemand ein Byte des Geheimtexts ändert, die Entschlüsselung laut fehlschlägt statt Müll zurückzugeben. Einfaches AES-CBC ohne einen separaten Authentifizierungsschritt gibt das nicht, und die Lücke hat echte Schwachstellen verursacht. Man kann den Roundtrip im AES Encrypt/Decrypt -Tool ausprobieren: einen String verschlüsseln und beobachten, dass genau dieselbe Passphrase zum Zurückholen benötigt wird, und eine falsche nichts gibt.
Symmetrisch vs. asymmetrisch in einem Abschnitt
Es gibt zwei Familien, und die Aufteilung betrifft, wie viele Schlüssel involviert sind.
Symmetrische Verschlüsselung verwendet einen gemeinsamen Schlüssel für das Sperren und Entsperren. AES ist der Standard hier. Es ist schnell und verarbeitet große Daten gut. Der Haken ist die Schlüsselverteilung: beide Seiten brauchen dasselbe Geheimnis, und dieses Geheimnis sicher an die andere Partei zu bringen ist ein eigenes Problem.
Asymmetrische Verschlüsselung verwendet ein Schlüsselpaar: einen öffentlichen Schlüssel, den jeder haben kann, und einen privaten Schlüssel, der geheim bleibt. Alles, was mit dem öffentlichen Schlüssel verschlüsselt wurde, kann nur mit dem privaten Schlüssel entschlüsselt werden. RSA-2048 (oder größer) und Elliptic-Curve-Verfahren leben hier. Das löst das Verteilungsproblem, weil man den öffentlichen Schlüssel frei herausgeben kann. Die Kosten sind Geschwindigkeit, daher wird in der Praxis asymmetrische Kryptografie selten verwendet, um Bulk-Daten zu verschlüsseln. TLS verwendet beispielsweise asymmetrische Kryptografie nur, um einen gemeinsamen symmetrischen Schlüssel auszuhandeln, und wechselt dann für den tatsächlichen Datenverkehr zu AES. Man erhält den Verteilungsvorteil des einen und die Geschwindigkeit des anderen.
Ein schneller Weg, sich zu merken, welches was ist: symmetrisch ist ein Schlüssel in beide Richtungen, asymmetrisch ist ein öffentlich/privat-Paar. Wenn man jemandem die Fähigkeit geben muss, ohne die Fähigkeit zum Entschlüsseln zu verschlüsseln, braucht man asymmetrisch.
Hashing: Einweg-Fingerabdrucke
Ein kryptografischer Hash nimmt beliebige Eingaben und erzeugt eine Ausgabe fester Größe. SHA-256 gibt immer 256 Bit zurück, also 64 Hex-Zeichen, egal ob man einen Buchstaben oder eine 4-GB-Videodatei einfüttert. Die Ausgabe ist deterministisch (gleiche Eingabe, gleicher Hash jedes Mal) und einwegig (man kann sie nicht rückwärts laufen, um die Eingabe zurückzubekommen).
Eingabe: "hello"
sha256: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Ein einzelnes Zeichen ändern und die Ausgabe sieht vollständig unzusammengehängend aus:
Eingabe: "Hello"
sha256: 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969
Dieser Lawineneffekt macht Hashing für Integrität nützlich. Eine Datei herunterladen, hashen, mit dem veröffentlichten Hash vergleichen, und man weiß, ob sich ein einzelnes Byte im Transit geändert hat. Hashing ist auch die Art, wie man etwas verifiziert, ohne es zu speichern: man hält den Fingerabdruck und später kann man prüfen, ob eine neue Eingabe übereinstimmt, ohne das Original je zu halten. Man kann SHA-256-Ausgaben mit dem Hash-Generator generieren und vergleichen.
Zwei wichtige Hinweise zu dem, welcher Algorithmus verwendet werden soll. MD5 und SHA-1 sind für jeden Sicherheitszweck gebrochen. Angreifer können zwei verschiedene Eingaben erstellen, die denselben MD5-Hash erzeugen, und SHA-1-Kollisionen wurden in der Praxis demonstriert. Wenn man einen der beiden in einem Sicherheitskontext zur Integritätsverifikation sieht, ist das ein Befund. SHA-256 oder SHA-3 verwenden. MD5 ist nur für nicht-adversarielle Prüfgrößensummen akzeptabel, wie das Erkennen versehentlicher Dateikorruption, bei der niemand aktiv versucht, einen zu täuschen.
Eines, was Hashing nicht ist: Verschlüsselung. Man kann einen Hash nicht “entschlüsseln”, weil es keinen Schlüssel gibt und die Originalinformation genuinen weg ist. Die Ausgabe ist kleiner und fester Größe; die Eingabe war beliebiger Größe. Die Zuordnung kann nicht invertiert werden. Was ein Angreifer tun kann, ist Eingaben raten, jede Rate hashen und nach einer Übereinstimmung suchen, was uns zu dem Teil bringt, wo der meiste Auth-Code falsch läuft.
Passwortspeicherung: wo SHA-256 zur Haftung wird
Hier ist das teuerste Missverständnis im gesamten Thema. Leute lernen, dass Hashing einwegig ist, schließen daraus, dass das Hashen eines Passworts sicher ist, greifen zu SHA-256 und liefern es aus. Das ist eine Schwachstelle, und ich habe es im Code-Review öfter markiert, als ich zählen kann.
Das Problem ist, dass SHA-256 schnell ist. Das ist ein Feature für Integritätsprüfungen und eine Katastrophe für Passwörter. Wenn eine Datenbank geleakt wird, hat der Angreifer jeden Passwort-Hash. Er muss nichts umkehren. Er führt Rateversuche durch dieselbe Hash-Funktion und vergleicht. Ein moderner GPU-Rig kann Milliarden von SHA-256-Hashes pro Sekunde berechnen. Die meisten menschlichen Passwörter fallen in Stunden.
Es wird schlimmer mit einfachem Hashing. Wenn zwei Nutzer dasselbe Passwort wählen, bekommen sie denselben Hash. Ein Angreifer hasht einmal ein Wörterbuch gängiger Passwörter, dann vergleicht diese vorberechnete Tabelle gleichzeitig mit jedem geleakten Hash. Diese vorberechneten Tabellen sind der Grund, warum man nicht einfach hashen und weggehen kann.
Zwei Dinge beheben das: Salt und Langsamkeit.
Salt
Ein Salt ist ein zufälliger Wert, eindeutig pro Nutzer, der vor dem Hashen in die Eingabe gemischt wird. Jetzt bekommen zwei Nutzer mit demselben Passwort unterschiedliche gespeicherte Hashes, was vorberechnete Tabellen sofort ausschaltet. Der Angreifer muss jedes Passwort einzeln angreifen statt alle auf einmal. Das Salt ist nicht geheim; es wird neben dem Hash gespeichert. Seine einzige Aufgabe ist es, jeden Hash einzigartig zu machen.
Eine absichtlich langsame KDF
Salt allein ist nicht genug, weil der Angreifer immer noch ein einzelnes gesaltetes Passwort schnell mahlen kann, wenn die Funktion schnell ist. Also hört man auf, eine schnelle Hash-Funktion zu verwenden, und wechselt zu einer Key-Derivation-Funktion, die absichtlich langsam und teuer ist. Die aktuellen guten Wahlmöglichkeiten:
- Argon2id - der moderne Standard. Abstimmbar nach Speicher, Zeit und Parallelität, und die Speicherkosten besiegen speziell GPU- und ASIC-Angriffe. Wenn man heute wählt, das wählen.
- scrypt - ebenfalls speicherschwer, eine solide Wahl und weitgehend verfügbar.
- bcrypt - älter, aber immer noch respektabel. Es hat einen Kostenfaktor (oft 10 bis 12), den man erhöht, wenn Hardware schneller wird. Beachte seine stille Einschränkung: es verwendet nur die ersten 72 Bytes der Eingabe.
- PBKDF2 - die konservativste Option. Es ist nicht speicherschwer, widersteht also GPUs weniger gut, aber es ist FIPS-genehmigt und überall, was der Grund ist, warum regulierte Umgebungen es immer noch mit einer hohen Iterationsanzahl verwenden. Man kann sehen, wie Iterationsanzahl und Salt in abgeleitete Ausgaben einfließen, mit dem PBKDF2 Hash Generator .
Das mentale Modell, das einen aus Ärger heraushält: eine schnelle Hash-Funktion wie SHA-256 ist für Integrität, wo man will, dass es schnell geht. Eine langsame KDF ist für Passwörter, wo Langsamkeit der eigentliche Punkt ist. Die Kosten so einstellen, dass ein legitimer Login etwa 100 bis 250 ms auf der eigenen Hardware dauert. Das ist für einen einmalig anmeldenden Nutzer unbemerkt und brutal für einen Angreifer, der Milliarden von Versuchen macht.
HMAC: Hashing plus einem Schlüssel, für Authentizität
Es gibt ein viertes Ding, auf das Menschen stoßen, und es sitzt zwischen Hashing und Verschlüsselung. HMAC ist ein geschlüsselter Hash. Man kombiniert einen geheimen Schlüssel mit der Nachricht und einer Hash-Funktion (HMAC-SHA-256 beispielsweise), und die Ausgabe beweist gleichzeitig zwei Dinge: die Nachricht wurde nicht verändert, und sie kam von jemandem, der den Schlüssel hält.
Warum nicht einfach die Nachricht hashen? Weil ein einfacher Hash nichts über den Produzenten beweist. Jeder kann einen SHA-256-Hash neu berechnen, also kann jeder die Nachricht manipulieren und einen passenden Hash neu berechnen. Mit HMAC kann ein Angreifer, der nicht den Schlüssel hat, keinen gültigen Tag erzeugen, also kann er die Nachricht nicht unentdeckt fälschlicherweise ändern oder fälschen.
Das ist das, was Webhook-Payloads signiert, was API-Anfrage-Signaturen schützt und was den Signaturteil von Token sichert. Wenn ein Zahlungsanbieter einen Webhook mit einem X-Signature-Header sendet, berechnet man den HMAC mit dem gemeinsamen Geheimnis neu und vergleicht. Wenn er übereinstimmt, ist die Nachricht authentisch. Man kann die Konstruktion mit dem HMAC Generator ausprobieren, indem man den Schlüssel ändert und beobachtet, wie sich der Tag vollständig ändert.
HMAC gibt Integrität und Authentizität. Es gibt keine Vertraulichkeit. Die Nachricht selbst ist immer noch lesbar; HMAC beweist nur, dass sie nicht manipuliert wurde. Wenn man auch den Inhalt verborgen haben möchte, verschlüsselt man zusätzlich.
Die Entscheidungstabelle
Das ist der Teil, der als Lesezeichen gesetzt werden sollte. Von dem ausgehend, was man tatsächlich will, dann das Werkzeug wählen.
| Ich möchte… | Verwenden | Nicht |
|---|---|---|
| Binärdaten durch nur-Text-Kanäle senden | Kodierung (Base64, Hex) | Irgendetwas, das sich “Verschlüsselung” nennt |
| Daten verbergen, aber später selbst zurücklesen | Symmetrische Verschlüsselung (AES-256-GCM) | Hashing, Kodierung |
| Anderen erlauben, an mich zu verschlüsseln, ohne ein Geheimnis zu teilen | Asymmetrische Verschlüsselung (RSA, ECC) | Symmetrisch |
| Benutzerpasswörter speichern | Langsame KDF (Argon2id, bcrypt, scrypt) | SHA-256, MD5, Kodierung |
| Prüfen, ob eine heruntergeladene Datei nicht korrumpiert oder verändert wurde | Schneller Hash (SHA-256) | Eine langsame KDF |
| Verifizieren, dass eine Nachricht unverändert von einem vertrauenswürdigen Absender kam | HMAC (HMAC-SHA-256) | Ein einfacher Hash |
| Versehentliche, nicht bösartige Dateikorruption erkennen | Prüfgrößensumme (CRC32, sogar MD5) | Eine langsame KDF |
Wenn man “brauche ich das Original zurück, und wer bekommt es” beantworten kann, wählt sich die Tabelle selbst.
Echte Fehler, die ich tatsächlich gesehen habe
Das sind keine Hypothesen. Jeder stammt aus echtem Code.
-
Base64-Passwörter, “verschlüsselt” genannt. Ein Dienst hat Benutzerpasswörter Base64-kodiert gespeichert und die Spalte
encrypted_passwordbenannt. Ein Datenbankexport und jedes Passwort war Klartext, weil Base64 keine Verschlüsselung ist. Das ist der klassische Kodierungs-als-Sicherheit-Fehler, und er ist erschreckend häufig. -
MD5 für die Passwortspeicherung. Eine Legacy-App hat Passwörter mit ungesalzenem MD5 gehasht. Nach einem Breach haben Angreifer die geleakten Hashes gegen vorberechnete Tabellen geführt und den Großteil davon innerhalb eines Tages wiederhergestellt. MD5 ist schnell und gebrochen; beide Probleme verstärken sich für Passwörter.
-
Verschlüsselt, wenn sie hashen meinten. Ein Team hat Passwörter mit AES verschlüsselt, damit sie Logins “verifizieren” konnten, indem sie entschlüsseln und vergleichen. Das bedeutet, das System kann jedes Passwort im Klartext zurückholen, was genau das ist, was man nicht will. Man braucht ein Passwort nie zurück. Man muss nur prüfen, ob ein Login-Versuch übereinstimmt. Das ist ein Hashing-Job, spezifisch ein langsamer-KDF-Job, kein Verschlüsselungsjob. Das Vorhandensein eines Entschlüsselungspfads ist der Fehler.
-
Daten gehasht, die sie zurückholen mussten. Das Spiegelbild des letzten. Ein System hat Kontonummern per SHA-256 gehasht, die es später anzeigen musste, und entdeckte dann, dass Hashes einwegig sind und die Daten nicht wiederherstellbar waren. Diese Daten brauchten Verschlüsselung, weil man sie zurückbraucht.
-
Einfacher Hash, wo HMAC hingehörte. Ein Webhook-Endpunkt hat Payloads verifiziert, indem er den Body gehasht und mit einem Header verglichen hat, ohne irgendein Geheimnis. Da jeder diesen Hash berechnen kann, kann jeder eine gültig aussehende Anfrage fälschen. Der Fix war HMAC mit einem gemeinsamen Geheimnis, damit nur der echte Absender einen passenden Tag erzeugen kann.
Das Muster über alle fünf ist dasselbe: jemand hat ein Werkzeug danach gewählt, wie die Ausgabe aussieht, statt nach dem Problem, das er löst.
Zusammenfassung
Drei Werkzeuge, drei Jobs. Kodierung formatiert Daten um und schützt nichts; wenn die Sorge ein Leser ist, der die Daten nicht sehen soll, leistet Kodierung keine Arbeit. Verschlüsselung verbirgt Daten, damit nur ein Schlüsselinhaber sie lesen kann, und man greift dazu, wann immer man das Original später zurückbraucht (AES-256-GCM für Shared-Key-Fälle, RSA oder ECC, wenn man kein Geheimnis teilen kann). Hashing erzeugt einen einwegigen Fingerabdruck fester Größe für Integrität und Verifikation, mit SHA-256 als dem Arbeitspferd und MD5 und SHA-1 aus allem Sicherheitsrelevanten zurückgezogen.
Passwörter sind der Sonderfall, der die meisten Menschen stolpert. Niemals einfach hashen, niemals verschlüsseln, niemals kodieren. Jedes salzen und durch eine langsame KDF wie Argon2id, bcrypt oder scrypt laufen lassen, abgestimmt, sodass ein einzelner Login einen Bruchteil einer Sekunde dauert und ein Milliarden-Versuch-Angriff für immer dauert. Und wenn man beweisen muss, dass eine Nachricht sowohl unverändert als auch von einer vertrauenswürdigen Quelle stammt, ist das HMAC: ein Hash mit einem Schlüssel. Nach dem Problem wählen, nicht nach dem Aussehen des Zeichensalats.
In diesem Artikel erwähnte Tools
- Hash Generator - Generate SHA-1, SHA-256, SHA-384 and SHA-512 hashes from text.
- HMAC Generator - Generate HMAC signatures with SHA-1, SHA-256, SHA-384 and SHA-512.
- AES-256 Encrypt / Decrypt Online - Free, In-Browser - Encrypt and decrypt text with AES-128, AES-192, or AES-256 in GCM, CBC, or CTR mode. PBKDF2 key derivation, entirely in your browser.
- Base64 Encoder & Decoder - Encode UTF-8 text to Base64 online or decode Base64 back to UTF-8 and plain text. Runs in your browser with no upload.
- PBKDF2 Hash Generator - Derive cryptographic keys from passwords using PBKDF2 with configurable iterations, salt and hash function.