Authentication

Password Hashing

Passwörter sind das eine Geheimnis, das man niemals im Klartext speichern sollte. Ein langsamer, gesalzener Hash verwandelt eine gestohlene Datenbank in einen Haufen von Vermutungen, die niemals enden.

intermediate14 min readUpdated 20. Sept. 2026
passwords.js
js
// passwords.js
import { hash, verify } from "@node-rs/argon2";

export const hashPassword = (password) => hash(password);

export async function verifyPassword(password, stored) {
  try {
    return await verify(stored, password);
  } catch {
    return false;
  }
}
Regel
Hashen, niemals verschlüsseln
Salt
Einzigartig pro Passwort
Empfohlen
Argon2id
Immer noch verbreitet
bcrypt
Vergleich
Timing-safe
Beim Login
Bei Bedarf rehashen

Warum es wichtig ist

Warum Hashing nicht optional ist

Von Haus aus Einweg

Ein Passwort-Hash kann nicht rückgängig gemacht werden. Eine geleakte Tabelle offenbart somit keinen Klartext, und ein Angreifer ist zu Offline-Guessing gezwungen.

Salts besiegen Vorberechnungen

Ein einzigartiger, zufälliger Salt pro Passwort macht Rainbow Tables nutzlos und stellt sicher, dass zwei identische Passwörter unterschiedliche Hashes ergeben.

Kosten sind das Ziel

Ein Work-Factor macht jeden Rate-Versuch so langsam, dass Brute-Force unpraktikabel wird. Dieser Faktor kann erhöht werden, sobald die Hardware schneller wird.

Das Gesamtbild

Die drei Grundpfeiler sicherer Credentials

Hashen, niemals verschlüsseln; jeden Digest salzen; und jeden Rate-Versuch bewusst teuer machen.

Hash

Transformieren

Eine Key-Derivation-Funktion verwandelt das Passwort in einen Digest fester Länge, der nicht umgekehrt werden kann.

Salt

Randomisieren

Ein zufälliger Wert, der neben dem Digest gespeichert wird, macht jeden Hash einzigartig und vereitelt vorberechnete Tabellen.

Verify

Vergleichen

Beim Login wird der Hash in konstanter Zeit neu abgeleitet und verglichen; anschließend wird der Hash aktualisiert, falls die Parameter veraltet sind.

HTML5 auf einen Blick

So sieht eine gute Speicherung von Credentials aus

Argon2id

Der moderne Standard mit anpassbarem Speicher, Iterationen und Parallelismus.

bcrypt

Bewährt und in den meisten Stacks integriert, mit einem Cost-Factor und einem Limit von 72 Bytes.

scrypt

Memory-hard und standardisiert; eine gute Wahl, wenn Argon2 nicht verfügbar ist.

Salt

Zufällig pro Passwort und im selben String wie der Digest gespeichert.

Timing-safe compare

Vergleich von Digests, ohne Informationen durch einen vorzeitigen Abbruch (Early Exit) preiszugeben.

Rehash beim Login

Erhöhung der Kosten oder Wechsel des Algorithmus, während sich Benutzer anmelden.

Eine kurze Geschichte

Von crypt zu memory-hard Hashes

  1. 1979

    crypt und DES

    Frühe Unix-Systeme hashen Passwörter mit einer 25-Runden-DES-Variante und einem 12-Bit-Salt.

    79
  2. 1999

    bcrypt

    Provos und Mazieres führen einen adaptiven, auf Blowfish basierenden Hash mit anpassbaren Kosten ein.

    99
  3. 2012

    scrypt

    Eine memory-hard Funktion wird veröffentlicht, um großflächiges paralleles Cracking teuer zu machen.

    12
  4. 2015

    Argon2 gewinnt die PHC

    Argon2id wird als Gewinner der Password Hashing Competition ausgewählt.

    15
  5. Heute

    Standardmäßig adaptiv

    Frameworks liefern Argon2 oder bcrypt aus und erwarten, dass Sie den Work-Factor optimieren.

    Heute

Der vollständige Leitfaden

Password Hashing: Alles was Sie wissen müssen

Warum Passwörter eine besondere Behandlung benötigen

Jedes andere Geheimnis in Ihrem System lässt sich rotieren: API keys, Tokens, Zertifikate. Passwörter sind anders. Sie liegen niemals in einer Form vor, die Sie regenerieren können, Nutzer verwenden sie auf verschiedenen Seiten immer wieder, und in dem Moment, in dem Ihre Datenbank leakt, hat der Angreifer alle Zeit der Welt, sie offline zu erraten.

Password Hashing ist die Praxis, eine Einweg-Transformation des Passworts anstelle des Passworts selbst zu speichern. Wenn sich ein Nutzer anmeldet, wenden Sie dieselbe Transformation an und vergleichen das Ergebnis. Sie erfahren niemals das Passwort, und das tut auch niemand, der die Tabelle stiehlt.

Dies ist kein Ort für Eigenkreationen. Die Regeln sind simpel und gut etabliert, und ihr Bruch ist der Grund, warum Datenlecks katastrophal werden.

Hashing ist keine Verschlüsselung

Verschlüsselung ist reversibel. Wenn Sie Passwörter verschlüsseln, befindet sich der Schlüssel irgendwo in Ihrem System. Jeder, der ihn in die Hände bekommt – durch ein Leak, ein Backup, eine falsch konfigurierte Umgebungsvariable oder einen Insider – kann sofort jedes Passwort lesen. Es gibt kein Szenario, in dem Sie das ursprüngliche Passwort benötigen, daher stellt die Reversibilität ein reines Risiko dar.

Eine Hash-Funktion nimmt einen Input entgegen und erzeugt einen Digest mit fester Länge; es gibt keinen praktischen Weg zurück. Für Passwörter reicht ein einfacher Hash jedoch nicht aus: Schnelle Funktionen wie SHA-256 sind darauf ausgelegt, schnell zu sein – genau das, was ein Angreifer will. Eine GPU kann Milliarden von Kandidaten pro Sekunde testen.

Sie benötigen eine Funktion, die absichtlich langsam und memory-hard ist, damit das Erraten von Passwörtern in großem Stil zu teuer wird.

Salt: Gleiches Passwort, unterschiedlicher Hash

Ohne einen Salt erzeugen zwei Benutzer mit dem Passwort hunter2 denselben Digest. Ein Angreifer kann eine riesige Tabelle mit gängigen Passwörtern und deren Hashes vorab berechnen – eine sogenannte Rainbow Table – und Treffer sofort finden. Zudem ist auf einen Blick erkennbar, welche Accounts dasselbe Passwort verwenden.

Ein Salt ist ein eindeutiger Zufallswert, der pro Passwort generiert und zusammen mit dem Digest gespeichert wird. Er ist nicht geheim; seine einzige Aufgabe ist es, jeden Hash einzigartig zu machen. Nun muss der Angreifer jeden Account einzeln angreifen, und Vorberechnungen sind nutzlos.

Anatomy of a stored Argon2 hash
$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$aBc...
algorithm + paramsalgorithm, version and work factors
saltunique per password, not secret
digestthe derived key you compare against

Moderne Bibliotheken generieren den Salt automatisch für Sie und kodieren ihn zusammen mit den Parametern in einem einzigen String. Diesen String speichern Sie einfach wortwörtlich ab.

Den richtigen Algorithmus wählen

Drei Funktionen sind wichtig zu kennen. Alle sind adaptiv: Die Kosten können erhöht werden, sobald die Hardware leistungsfähiger wird.

Argon2id

Der Gewinner der Password Hashing Competition 2015 und die aktuelle Empfehlung. Er ist „memory-hard“, was bedeutet, dass ein Angreifer neben Rechenleistung auch Speicher kaufen muss, wodurch der Vorteil von GPUs und ASICs abgeschwächt wird. Argon2id bietet eine gute Balance zwischen der Resistenz gegen Side-Channel- und GPU-Angriffe und ist der richtige Standard für neue Systeme.

scrypt

Standardisiert in RFC 7914 und ebenfalls memory-hard, ist scrypt eine solide Wahl, wenn Argon2 nicht verfügbar ist. Er bietet Parameter für Kosten, Blockgröße und Parallelität, sodass er feinjustiert werden kann, allerdings lassen sich diese Parameter leichter falsch konfigurieren als die von Argon2.

bcrypt

Veröffentlicht im Jahr 1999 und nach wie vor weit verbreitet, ist bcrypt gut verstanden und praxiserprobt. Sein Kostenfaktor verdoppelt den Aufwand mit jeder Erhöhung. Der wichtigste Vorbehalt ist, dass er die Eingabe bei 72 Bytes abschneidet, sodass sehr lange Passphrasen stillschweigend gekürzt werden – in diesem Fall sollten Sie die Eingabe vor dem Hashing vorverarbeiten.

Egal, wofür Sie sich entscheiden: Greifen Sie niemals zu MD5, SHA-1 oder einfachem SHA-256. Sie sind das falsche Werkzeug, und das Stapeln von Runden darauf ist ein Eigenbau-Schema, dem niemand vertrauen sollte.

Den Work Factor optimieren

Die Kosten müssen zwei gegensätzliche Anforderungen in Einklang bringen. Ist der Wert zu niedrig, ist der Hash anfällig für Brute-Force-Angriffe; ist er zu hoch, kann eine Flut von Login-Versuchen zu einem Denial-of-Service-Vektor gegen die eigene CPU werden.

Optimieren Sie den Wert so, dass ein einzelner Hash auf Ihrer Produktionshardware etwa 100 bis 500 Millisekunden benötigt. Messen Sie den Login-Endpoint unter realistischer Last und nicht nur in einer Benchmark-Schleife, und legen Sie den Wert basierend auf diesen Daten fest. Überprüfen Sie dies anschließend regelmäßig: Da die Hardware stetig besser wird, verlieren dieselben Parameter jedes Jahr an Sicherheit.

// Argon2id parameters (memory in KiB, iterations, parallelism)
await hash(password, { memoryCost: 19456, timeCost: 2, parallelism: 1 });

Speichern und Verifizieren

Die Registrierung und der Login sind die einzigen beiden Stellen, an denen Sie ein Passwort verarbeiten.

import { hash, verify } from "@node-rs/argon2";

// Registration: hash before the password ever reaches the database.
const digest = await hash(password);
await db.users.insert({ email, passwordHash: digest });

// Login: verify against the stored digest.
const user = await db.users.findByEmail(email);
const ok = user && (await verify(user.passwordHash, password));
if (!ok) return res.status(401).json({ error: "invalid_credentials" });

Geben Sie denselben Fehler zurück, unabhängig davon, ob die E-Mail oder das Passwort falsch war. Eine Nachricht wie „Benutzer nicht gefunden“ ermöglicht es einem Angreifer, Accounts durch Enumeration zu identifizieren.

Timing-Attack und sicherer Vergleich

Der Vergleich von zwei Hashes mit === bricht beim ersten unterschiedlichen Byte ab. Die benötigte Zeit hängt daher davon ab, wie viele Teile der Vermutung korrekt waren. Ein geduldiger Angreifer kann dies messen, um einen Wert zu rekonstruieren. Es ist zwar ein schmaler Kanal, aber gerade beim Passwortvergleich ist dies entscheidend.

Nutzen Sie die Verifizierungsfunktion der Library, die den Vergleich in konstanter Zeit (constant time) durchführt. Vergleichen Sie Digests niemals selbst und vergleichen Sie niemals das Passwort im Klartext.

Hashes im Laufe der Zeit aktualisieren

Ihre Anforderungen werden sich ändern: Die Rechenkosten steigen oder Sie migrieren von bcrypt zu Argon2. Da Sie den Klartext niemals gespeichert haben, können Sie das Re-Hashing nur beim Login durchführen, wenn das Passwort kurzzeitig im Speicher liegt.

Speichern Sie den Algorithmus und die Parameter zusammen mit jedem Digest – der kodierten Zeichenfolge gelingt dies bereits – und prüfen Sie diese bei jedem erfolgreichen Login:

if (await verify(stored, password)) {
  if (needsRehash(stored)) {
    await db.users.update(user.id, { passwordHash: await hash(password) });
  }
  // continue the login
}

Benutzer, die sich nie wieder anmelden, behalten ihren alten Hash, was völlig in Ordnung ist; sie sind dadurch immer noch geschützt. Im Laufe der Zeit migrieren die aktiven Konten ganz natürlich.

Passwortrichtlinien in der Praxis

Der Algorithmus schützt Sie nach einem Datenleck. Eine gute Richtlinie verringert die Wahrscheinlichkeit, dass eines überhaupt passiert.

  • Bevorzugen Sie Länge gegenüber Komplexität. Eine Passphrase schlägt P@ssw0rd!.
  • Prüfen Sie neue Passwörter gegen Listen bekannter Leaks, wie zum Beispiel die k-anonymity API von Have I Been Pwned.
  • Erzwingen Sie keine willkürlichen Rotationen. Ein erzwungenes Ablaufdatum führt zu Summer2026! Mustern.
  • Implementieren Sie Rate-Limiting für Login- und Reset-Versuche und fügen Sie ein exponentielles Backoff oder einen Lockout hinzu.
  • Nutzen Sie einen generischen Reset-Flow mit einmaligen, auslaufenden Tokens und versenden Sie Passwörter niemals per E-Mail.

Best Practices

  • Verwenden Sie Argon2id, scrypt oder bcrypt zum Hashen — niemals einen schnellen Allzweck-Hash.
  • Überlassen Sie der Library das Generieren und Speichern des Salts.
  • Optimieren Sie die Cost-Parameter auf 100–500 ms und erhöhen Sie diese im Laufe der Zeit.
  • Verifizieren Sie Passwörter mit der Constant-Time-Funktion der Library.
  • Führen Sie bei einem erfolgreichen Login ein Rehash durch, wenn sich die Parameter geändert haben.
  • Geben Sie generische Fehlermeldungen zurück und implementieren Sie ein Rate-Limit für Versuche.
  • Führen Sie das Hashen ausschließlich auf dem Server aus; niemals im Client.

Häufige Fehler

  • Passwörter zu verschlüsseln oder sie “vorübergehend” im Klartext zu speichern.
  • SHA-256 oder MD5 mit nur wenigen Durchläufen zu verwenden und dies als Hashing zu bezeichnen.
  • Ein einziger globaler Salt oder die Wiederverwendung desselben Salts über mehrere Accounts hinweg.
  • Das Vergleichen von Hashes mit == oder ===.
  • Passwörter in Logs zu schreiben oder sie in Fehlerberichten mitzuführen.
  • Die maximale Passwortlänge zu niedrig anzusetzen, was die Verwendung von Passphrasen erschwert.
  • Zu vergessen, den Cost Factor über Jahre hinweg zu aktualisieren.

Wie geht es weiter?

Anmeldedaten sind nur die erste Hürde. Sobald ein Benutzer verifiziert ist, benötigen Sie einen Ort, an dem dieser Zustand gespeichert wird: Lesen Sie den Guide zu Session Auth für cookie-basierte Sessions oder den JWT-Guide für zustandslose Tokens. Wenn Sie Passwörter lieber gar nicht selbst verwalten möchten, delegieren OAuth 2.0 und OpenID Connect den Login an einen Identity Provider.

In der Praxis

Hashen, Speichern, Verifizieren

Die gleichen drei Schritte in jedem Stack. Die Library übernimmt den Salt und kodiert ihn zusammen mit dem Digest.

hash.js
import { hash } from "@node-rs/argon2";

const digest = await hash(password);
// $argon2id$v=19$m=19456,t=2,p=1$...$...

Passwort hashen

Verwenden Sie einen zweckgebundenen Passwort-Hash mit einem Work-Factor. Ein schneller Allzweck-Hash ist das Lieblingsgeschenk eines Crackers.

Bevorzugt
import { hash } from "@node-rs/argon2";

const digest = await hash(password);
Vermeiden
import { createHash } from "node:crypto";

const digest = createHash("sha256")
  .update(password)
  .digest("hex");

Passwort vergleichen

Vergleichen Sie über die Constant-Time-Prüfung der Library. Einfache String-Gleichheit kann Inhalt und Länge durch Timing-Unterschiede preisgeben.

Bevorzugt
const ok = await verify(stored, password);
Vermeiden
const ok = stored === (await hash(password));

Abwägungen

Argon2 oder bcrypt?

Beide sind sicher, wenn sie richtig konfiguriert sind. Argon2 ist der moderne Standard; bcrypt bleibt eine hervorragende Wahl mit jahrelanger Erfahrung im Produktionseinsatz.

Strengths

  • Argon2id ist memory-hard

    Es widersteht GPU- und ASIC-Cracking, da es sowohl Speicher als auch Zeit benötigt, und ist die aktuelle Empfehlung.

  • bcrypt ist allgegenwärtig

    Es ist in den meisten Frameworks integriert, hat eine lange Historie und benötigt keine zusätzlichen nativen Abhängigkeiten.

  • Beide sind anpassbar

    Der Work-Factor kann im Laufe der Zeit erhöht werden, sodass gespeicherte Hashes gestärkt werden können, ohne die Passwörter der Benutzer zu ändern.

Trade-offs

  • bcrypt kürzt bei 72 Bytes

    Lange Passphrasen werden stillschweigend abgeschnitten. Nutzen Sie daher Pre-Hashing oder bevorzugen Sie Argon2, wenn dies relevant ist.

  • Tuning erfordert Sorgfalt

    Zu niedrig ist es knackbar; zu hoch wird der Login unter Last zu einem Denial-of-Service-Vektor.

  • Hashing ist nicht alles

    Rate Limiting, Breach-Checks und ein sicherer Reset-Flow sind genauso wichtig wie der Algorithmus selbst.

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Password Hashing & Credentials zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Password Hashing & Credentials — mit Quizzen und echtem Code, den Sie im Browser ausführen können.