Jede Offenlegung eines Datenschutzverstoßes folgt dem gleichen Bogen: “Wir nehmen Sicherheit ernst.” Dann lesen Sie die Details und stellen fest, dass Passwörter im Klartext gespeichert wurden, mit MD5 gehasht oder mit einem Schlüssel verschlüsselt wurden, der sich in derselben Datenbank befand. Das Problem der Passwortspeicherung ist gelöst — die Algorithmen existieren, die Bibliotheken existieren, die Dokumentation existiert — und doch machen Entwickler es immer noch falsch.
Dieser Beitrag behandelt, was gespeichert werden sollte, was nicht gespeichert werden sollte, den Unterschied zwischen Hashing und Verschlüsselung und warum SHA-256(password) kein Passwort-Hash ist.
Die Regel: niemals Klartext speichern
Wenn Ihre Datenbank kompromittiert wird und Sie Klartext-Passwörter haben, ist jeder Benutzer, der dieses Passwort auf anderen Seiten wiederverwendet, jetzt kompromittiert. Nicht nur Ihre Seite — ihre E-Mail, ihre Bank, ihre sozialen Medien. Klartext-Passwortspeicherung ist kein Sicherheitsproblem. Es ist ein moralisches Problem.
Speichern Sie immer einen Hash.
Hashing vs. Verschlüsselung
Hashing ist eine Einwegtransformation. Sie können es nicht umkehren. Bei hash(password) können Sie password nicht wiederherstellen. Sie können nur verifizieren: Eingabe hashen und vergleichen.
Verschlüsselung ist eine Zwei-Wege-Transformation. Bei encrypt(password, key) können Sie password mit dem Schlüssel wiederherstellen. Wenn der Schlüssel kompromittiert wird, sind alle Passwörter kompromittiert.
Hashen Sie Passwörter. Verschlüsseln Sie sie niemals. Wenn Sie den Klartext wiederherstellen müssen (was Sie nicht müssen), lösen Sie das falsche Problem.
Warum SHA-256 nicht ausreicht
Ein häufiger Fehler: hash = SHA-256(password). Es ist schnell, es ist bekannt und es ist falsch für Passwörter.
Das Problem ist die Geschwindigkeit. SHA-256 auf einer modernen GPU verarbeitet Milliarden von Hashes pro Sekunde. Ein Angreifer mit einer Datenbank von SHA-256-Passwort-Hashes kann jedes gängige Passwort, jedes Wörterbuchwort, jede Permutation in Minuten ausprobieren.
Passwort-Hashing-Algorithmen sind absichtlich langsam. Sie verwenden Schlüsseldehnung — Tausende von Iterationen einer Hash-Funktion — um Brute-Force-Angriffe unpraktikabel zu machen:
- bcrypt — der Standard seit 1999. Anpassbarer Kostenfaktor. Passwort + Salt → Hash. Jede bedeutende Sprache hat eine Bibliothek.
- scrypt — speicherhart. Benötigt große Mengen an RAM zur Berechnung, was GPU-Angriffe erschwert.
- Argon2 — Gewinner des Passwort-Hashing-Wettbewerbs (2015). Speicherhart, konfigurierbare Zeit- und Speicherkosten. Die aktuelle Best Practice.
- PBKDF2 — von NIST empfohlen, weit verbreitet, verwendet HMAC-Iterationen. Weniger speicherhart als Argon2, aber gut getestet.
Verwenden Sie bcrypt, scrypt oder Argon2. Erfinden Sie nicht Ihr eigenes Schema. Hashen Sie nicht einmal mit SHA-256 und nennen Sie es erledigt.
Salz: warum jedes Passwort ein einzigartiges braucht
Ein Salz sind zufällige Daten, die dem Passwort vor dem Hashing hinzugefügt werden. Ohne Salz erzeugen zwei Benutzer mit demselben Passwort denselben Hash. Ein Angreifer kann Hashes für gängige Passwörter vorberechnen (eine Rainbow-Tabelle) und sie mit Ihrer Datenbank abgleichen.
Mit einem einzigartigen Salz pro Benutzer ist jeder Hash anders, selbst wenn die Passwörter identisch sind. Rainbow-Tabellen funktionieren nicht. Der Angreifer muss jedes Passwort einzeln mit Brute-Force angreifen.
Moderne Passwort-Hashing-Funktionen (bcrypt, Argon2) generieren automatisch ein zufälliges Salz. Sie müssen Salze nicht separat verwalten. Übergeben Sie das Passwort an die Hash-Funktion, und sie erledigt den Rest.
Was gespeichert werden sollte
Für jeden Benutzer speichern Sie:
- Den Passwort-Hash (bcrypt/Argon2-Ausgabe, die das Salz und die Kostenparameter enthält)
- Den Hash-Algorithmus (bcrypt, argon2 usw.)
- Die Kostenparameter (bcrypt-Runden, Argon2-Speicher/Zeit)
Speichern Sie NICHT:
- Das Klartext-Passwort
- Das Salz separat (die Hash-Funktion-Ausgabe enthält es)
- Einen “Verschlüsselungsschlüssel”, der das Passwort entschlüsseln könnte
- Passwort-Hinweise (sie geben Informationen preis)
Die Hash-Ausgabe von bcrypt sieht so aus: $2b$12$LJ3m4ys3GzF4VQ.YOUR.HASH.HERE. Das $2b$ identifiziert den Algorithmus, 12 ist der Kostenfaktor, und der Rest ist Salt + Hash. Um einen Login-Versuch zu überprüfen, hashen Sie das eingegebene Passwort mit denselben Parametern und vergleichen es.
Kostenfaktoren
Der Kostenfaktor von bcrypt bestimmt, wie langsam der Hash ist. Jede Erhöhung verdoppelt die Berechnungszeit:
- Kosten 10: ~10ms pro Hash (schnell für Benutzer, schnell für Angreifer)
- Kosten 12: ~40ms pro Hash (angemessen)
- Kosten 14: ~160ms pro Hash (langsam für Angreifer, merkbar für Benutzer)
- Kosten 16: ~640ms pro Hash (zu langsam für die meisten Login-Abläufe)
Der Sweet Spot in 2026: bcrypt Kosten 12-14, Argon2 mit 64MB+ Speicher und 3+ Iterationen. Das Ziel ist es, jeden Hash-Versuch so langsam zu machen, dass das Brute-Forcen von Milliarden von Passwörtern Jahre dauert, aber so schnell, dass ein Benutzer bei der Anmeldung keine Verzögerung bemerkt.
Rate-Limiting
Hashing allein reicht nicht aus. Wenn ein Angreifer Millionen von Passwörtern pro Sekunde gegen Ihren Login-Endpunkt ausprobieren kann, wird selbst bcrypt Sie nicht retten.
Begrenzen Sie Login-Versuche. Nach 5-10 fehlgeschlagenen Versuchen von derselben IP oder demselben Konto sperren Sie das Konto vorübergehend oder erfordern ein CAPTCHA. Dies begrenzt Brute-Force-Angriffe auf ein Minimum.
Enthüllen Sie nicht, ob Benutzername oder Passwort falsch waren. “Ungültiger Benutzername oder Passwort” ist sicherer als “Kein Konto mit dieser E-Mail gefunden.” Letzteres sagt einem Angreifer, welche Benutzernamen existieren.
Kontosperrung nach anhaltenden Fehlern hinzufügen. Nach 10-20 fehlgeschlagenen Versuchen das Konto für 15 Minuten sperren oder eine E-Mail-Verifizierung zum Entsperren erfordern.
Passwortgenerierung
Bei der Generierung von Passwörtern (für Benutzer, API-Schlüssel, Tokens):
- Minimum 12 Zeichen. 8 reicht nicht mehr gegen moderne Hardware aus.
- Großbuchstaben, Kleinbuchstaben, Ziffern und Symbole einbeziehen. Aber Länge ist wichtiger als Komplexität.
- Verwenden Sie einen CSPRNG (kryptographisch sicheren Pseudo-Zufallszahlengenerator).
Math.random()ist nicht sicher. - Generieren Sie niemals vorhersagbare Muster.
Password1!sieht komplex aus, ist aber ratenfach erratbar.
Das beste Passwort ist eine zufällig generierte Zeichenkette von einem CSPRNG. Verwassen Sie einen Passwort-Manager zur Speicherung. Bitten Sie Benutzer nicht, komplexe Passwörter zu merken — geben Sie ihnen ein generiertes und lassen Sie den Manager es verwalten.
Häufige Fehler
- Einmaliges Hashing mit SHA-256 oder MD5 — zu schnell, kein Salz, trivial brute-forcebar.
- Verwendung eines statischen Salzes — wenn alle Benutzer denselben Salz teilen, funktionieren Rainbow-Tabellen.
- Separate Salzspeicherung — wenn das Salz in einer anderen Tabelle ist, erhält ein Angreifer, der die Datenbank kompromittiert, beides.
- Verschlüsseln statt Hashen — wenn Sie das Passwort entschlüsseln können, kann es ein Angreifer mit dem Schlüssel auch.
- Kein Rate-Limiting — selbst bcrypt ist anfällig für unbegrenzte Login-Versuche.
- Offenlegung, ob ein Benutzername existiert — hilft Angreifern, gültige Konten zu enumerieren.
- Verwendung von
Math.random()zur Passwortgenerierung — vorhersagbar, nicht kryptographisch sicher.
Schnellreferenz
| Tun | Nicht tun |
|---|---|
| bcrypt, Argon2 oder scrypt verwenden | SHA-256 oder MD5 verwenden |
| Bibliothek das Salz-Handling überlassen | Salze selbst verwalten |
| Login-Versuche begrenzen | Unbegrenzte Passwortversuche erlauben |
| Passwörter mit CSPRNG generieren | Math.random() verwenden |
| Nur den Hash speichern | Klartext- oder verschlüsselte Passwörter speichern |
| “Ungültige Anmeldeinformationen” sagen | “Falsches Passwort” sagen |
Ausprobieren
Wenn Sie ein sicheres zufälliges Passwort generieren oder eine Zeichenkette für Tests hashen müssen, verwenden Sie ein browserbasiertes Tool, damit die Operation lokal bleibt. Ihr Passwort sollte niemals für eine einfache Generierungsaufgabe einen Server berühren.