bcrypt vs Argon2 vs scrypt: Passwort-Hashing 2026
Kurze Antwort: Für jedes neue Projekt 2026 nehmen Sie Argon2id mit m=19456, t=2, p=1. Das ist die Vorgabe des OWASP Password Storage Cheat Sheets, und es ist der Passwort-Hash mit dem stärksten Schutz gegen GPU- und Seitenkanalangriffe, den Sie heute ausliefern können.
Falls Argon2 in Ihrem Stack fehlt (selten, kommt aber bei manchen Embedded- oder älteren Laufzeiten vor), nehmen Sie scrypt mit N=2^17, r=8, p=1. Setzen Sie bcrypt mit cost=12 nur ein, wenn Sie an ein Altsystem gebunden sind, das bcrypt schon nutzt, und keine neue Abhängigkeit einführen können. Bleiben Sie bei PBKDF2-HMAC-SHA-256 mit 600.000 Iterationen, sofern FIPS-140-Konformität zwingend ist.
| Algorithmus | OWASP-2026-Parameter | Wann wählen |
|---|---|---|
| Argon2id | m=19456 KiB, t=2, p=1 | Standard für neue Projekte |
| scrypt | N=2^17, r=8, p=1 | Argon2 nicht verfügbar |
| bcrypt | cost=12 (min. 10) | Nur Altsysteme |
| PBKDF2 | HMAC-SHA-256, 600k Iterationen | FIPS-140 erforderlich |
Der Rest dieses Artikels erklärt, warum genau diese Zahlen, wie Sie sie an Ihre Hardware anpassen und wie Sie migrieren, ohne ein Passwort-Reset zu erzwingen. Wenn Sie starke Testpasswörter zum Benchmarken brauchen, nutzen Sie den Zufallspasswort-Generator. Den größeren Kontext liefert der Leitfaden zu Web-Sicherheit.
Warum Passwort-Hashing anders ist als allgemeines Hashen
Hashfunktionen sehen von außen alle gleich aus: Daten gehen rein, ein Digest fester Länge kommt raus, und umkehren lässt sich nichts. Aber die Designziele für „diese 4-GB-ISO hashen” und „dieses 12-stellige Passwort hashen” sind exakt entgegengesetzt. Das eine soll so schnell sein, wie es das Silizium hergibt. Das andere soll so langsam sein, wie es Ihr Login-Latenzbudget verträgt.
Wer diese beiden Welten verwechselt, baut sich den Weg, auf dem aus Datenlecks Account-Übernahmen werden.
Warum MD5 und SHA-256 für Passwörter nicht reichen
Universal-Hashes wie MD5, SHA-1 und SHA-256 wurden auf Durchsatz hin entworfen. Sie verarbeiten Gigabyte pro Sekunde auf gewöhnlichen CPUs und Dutzende Gigabyte pro Sekunde auf GPUs. Für Datei-Prüfsummen und Content Addressing ist das super; für Passwörter ist es eine Katastrophe.
Hashcat-Benchmarks auf einer einzelnen RTX 4090 zeigen 2024 rund 164 GH/s für MD5 und 22 GH/s für SHA-256. Ein achtstelliges Passwort aus Kleinbuchstaben und Ziffern (36^8 ≈ 2,8 × 10^12 Kandidaten) fällt einer einzelnen GPU gegen MD5 in unter einer Minute zum Opfer und gegen SHA-256 in unter zwei Minuten. Eine kompromittierte Datenbank, die sha256(password) speichert, ist faktisch Klartext.
Salt rettet Sie hier ebenfalls nicht. Salt verhindert vorausberechnete Rainbow-Tables, bremst aber einen Angriff pro Account in keiner Weise: Der Angreifer hasht jeden Kandidaten einfach zusammen mit dem geleakten Salt.
Für nicht-sicherheitskritische Prüfsummen sind MD5 und SHA-256 weiter nützlich. Genau dafür gibt es Werkzeuge wie den MD5 & SHA-256 Hash-Generator. Eine ausführliche Gegenüberstellung der Einsatzgebiete steht unter MD5 vs SHA-256. Für Passwörter brauchen Sie aber einen Hash, der bewusst langsam ist.
Die drei Eigenschaften eines modernen Passwort-Hashes
Ein Passwort-Hash, der 2026 produktionsreif ist, hat drei Eigenschaften:
- Bewusst langsam, mit anpassbarem Arbeitsfaktor. Ein Login soll 100–500 ms dauern. Schnell genug, dass Nutzer es nicht spüren, langsam genug, dass ein Offline-Angreifer Tage pro Million Versuche verbrennt. Der Arbeitsfaktor muss als Parameter regelbar sein, damit Sie ihn mit fortschreitender Hardware nachziehen können.
- Salt pro Datensatz. Ein eindeutiger Zufalls-Salt pro Passwort vereitelt Rainbow-Tables und zwingt den Angreifer, jeden Account einzeln anzugreifen. Moderne Algorithmen erzeugen den Salt für Sie und betten ihn in die Ausgabezeichenkette ein.
- Speicherhart (memory-hard). GPUs und ASICs sind rechenstark, aber teuer bei hochbandbreitigem Speicher. Ein Algorithmus, der pro Hash zig MiB benötigt, zwingt Angreifer dazu, RAM proportional zu ihrer Parallelität bereitzustellen, und macht GPU-Farmen unwirtschaftlich.
bcrypt erfüllt (1) und (2), aber nicht (3). scrypt war der erste Algorithmus, der alle drei abdeckt. Argon2 hat das Design verfeinert und die Password Hashing Competition gewonnen. Der nächste Abschnitt nimmt sich jeden Kandidaten der Reihe nach vor.
Die drei Algorithmen — Architektur und Kompromisse
bcrypt — Blowfish-basiert, zeit-hart
bcrypt wurde 1999 von Niels Provos und David Mazières für OpenBSD entworfen. Er baut auf der Blowfish-Chiffre auf, mit einer aufwändigen Schlüssel-Setup-Phase („EksBlowfish”), die 2^cost-mal wiederholt wird. Der einzige stellbare Parameter ist der Cost Factor (auch „log rounds” genannt): Jede Erhöhung um 1 verdoppelt die Arbeit. Ein cost=10-Hash führt 1.024 Schlüsselpläne aus, cost=14 führt 16.384 aus.
Ein bcrypt-Hash sieht so aus:
$2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
│ │ │ │
│ │ │ └─ 31-Zeichen base64-Hash
│ │ └─ 22-Zeichen base64-Salt
│ └─ Cost Factor (12)
└─ Algorithmus-Kennung ($2b$ = bcrypt v2)
Das Format ist selbstbeschreibend: verify() liest Cost und Salt aus der gespeicherten Zeichenkette, separate Spalten sind nicht nötig.
Die Schwächen sind real. Der Speicherbedarf von bcrypt liegt bei rund 4 KiB, klein genug, dass eine High-End-GPU Tausende von bcrypt-Kernen parallel laufen lässt. Außerdem kürzt bcrypt Eingaben ab 72 bytes stillschweigend ab. Eine Passphrase mit 100 Zeichen hat dieselbe Sicherheit wie ihre ersten 72 bytes. Der maximale Cost ist 31, doch ab etwa 16 wird die Login-Latenz auf üblicher Hardware schmerzhaft.
scrypt — der Pionier des speicherharten Hashings
scrypt wurde 2009 von Colin Percival für den Backup-Dienst Tarsnap veröffentlicht und 2016 als RFC 7914 standardisiert. Er führte die Idee der Speicherhärte ein: Der Algorithmus füllt einen großen Puffer mit pseudozufälligen Daten und liest dann an zufälligen Positionen, was jede Implementierung zwingt, den Speicher auch wirklich zu allokieren.
scrypt nimmt drei Parameter:
- N — CPU-/Speicherkosten (muss eine Zweierpotenz sein)
- r — Blockgröße in bytes (Multiplikator für Speicher und Mischrunden)
- p — Parallelität (unabhängige Berechnungen, meist genutzt, um CPU-Zeit ohne Speicher zu skalieren)
Der Speicherverbrauch beträgt etwa 128 × N × r bytes. Mit OWASPs Empfehlung N=2^17, r=8 ergibt das 128 × 131072 × 8 = 134.217.728 bytes, also 128 MiB pro Hash.
scrypt ist außerdem eine Schlüsselableitungsfunktion, nicht nur ein Passwort-Hash. Er kommt in Kryptowährungs-Wallets, in der vollständigen Festplattenverschlüsselung und im ursprünglichen Litecoin-Proof-of-Work zum Einsatz. Diese Doppelrolle ist praktisch, wenn Sie Passwortspeicherung und Schlüsselableitung in einer Bibliothek brauchen.
Argon2 (id/i/d) — Sieger der Password Hashing Competition
Die Password Hashing Competition lief von 2013 bis 2015 und bewertete 24 Kandidatenalgorithmen anhand von Speicherhärte, Seitenkanalresistenz und Implementierungseinfachheit. Argon2 hat gewonnen. Standardisiert wurde er 2021 als RFC 9106.
Argon2 hat drei Varianten. Sie unterscheiden sich darin, wie der Speicher beim Mischen adressiert wird:
- Argon2d verwendet datenabhängige Speicheradressen. Das maximiert die Resistenz gegen GPU- und ASIC-Angriffe, leakt aber Informationen über Cache-Timing-Seitenkanäle. Geeignet für Kryptowährungs-Proof-of-Work, nicht für Authentifizierung.
- Argon2i verwendet datenunabhängige Adressen. Seitenkanalsicher, aber etwas schwächer gegen GPU-Tradeoff-Angriffe.
- Argon2id ist ein Hybrid: Die erste Hälfte des ersten Durchlaufs nutzt Argon2i-Indexierung (seitenkanalsicher), der Rest nutzt Argon2d-Indexierung (GPU-resistent). RFC 9106 empfiehlt Argon2id ausdrücklich für Passwort-Hashing, OWASP ebenso.
Argon2 nimmt drei Parameter:
- m — Speicher in KiB
- t — Time Cost (Anzahl der Durchläufe über den Speicherpuffer)
- p — Parallelität (Anzahl der gleichzeitig verarbeiteten Lanes)
Ein Argon2id-Hash nutzt das PHC-String-Format und sieht so aus:
$argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG
Wie bei bcrypt sind alle Parameter in der Zeichenkette eingebettet, sodass verify() keine Parametertabelle braucht.
OWASP-2026-Empfehlungsparameter
Das OWASP Password Storage Cheat Sheet ist die kanonische Referenz. Die folgenden Zahlen entsprechen seinem aktuellen Stand. Sie sind konservativ, bemessen für einen typischen Webserver mit 100–500 ms Login-Latenzbudget, und Sie sollten vor dem Ausliefern dennoch auf Ihrer eigenen Hardware benchmarken.
Argon2id-Parameter: erste Wahl
OWASPs Basisempfehlung: m=19456 (19 MiB), t=2, p=1.
Wenn Ihr Server mehr RAM-Spielraum hat, können Sie die Arbeit zwischen Speicher und Zeit verschieben. RFC 9106 nennt äquivalente Profile; OWASP empfiehlt eines der folgenden:
| memoryCost (m) | timeCost (t) | parallelism (p) | RAM pro Hash |
|---|---|---|---|
| 47104 | 1 | 1 | 46 MiB |
| 19456 | 2 | 1 | 19 MiB (Basis) |
| 12288 | 3 | 1 | 12 MiB |
| 9216 | 4 | 1 | 9 MiB |
| 7168 | 5 | 1 | 7 MiB |
Faustregel zum Tuning. Legen Sie zuerst m anhand Ihres RAM-Budgets bei Spitzen-Logins fest. Wenn Sie 100 gleichzeitige Logins erwarten und 4 GiB übrig haben, sind das 40 MiB pro Hash. Erhöhen Sie dann t, bis ein einzelner Verify auf Ihrer Produktions-CPU 100–500 ms dauert. Lassen Sie p=1, sofern Sie keinen konkreten Multi-Core-Grund haben, daran zu drehen (die meisten Web-Frameworks geben jeder Anfrage ohnehin einen eigenen Thread).
scrypt-Parameter: wenn Argon2 nicht verfügbar ist
OWASPs Empfehlung: N=2^17 (131072), r=8, p=1, was 128 MiB pro Hash bedeutet.
Wenn 128 MiB pro gleichzeitigem Login zu viel für Ihren Server sind, lässt OWASP schwächere Profile zu:
| N | r | p | RAM pro Hash |
|---|---|---|---|
| 2^17 | 8 | 1 | 128 MiB (bevorzugt) |
| 2^16 | 8 | 1 | 64 MiB |
| 2^15 | 8 | 1 | 32 MiB |
N muss eine Zweierpotenz sein. Ein größeres r erhöht Speicher und CPU-Arbeit proportional, ein größeres p erhöht die CPU-Arbeit ohne den Speicher pro Instanz zu vergrößern. Für Passwort-Hashing belassen Sie r und p auf den Defaults und tunen nur N.
bcrypt: Cost Factor 10+ nur für Altsysteme
OWASP empfiehlt bcrypt für neue Projekte nicht mehr, aber er ist überall: Devise, Spring Security, ASP.NET Identity und unzählige selbstgebaute Auth-Systeme nutzen ihn als Default.
Wenn Sie an bcrypt gebunden sind, gelten diese Regeln:
- Mindest-bcrypt-Cost-Factor: 10. Darunter ist es so schnell, dass eine einzelne GPU eine geleakte Datenbank in Tagen abarbeitet.
- Empfohlen: 12 bis 14, je nach Hardware. Auf einem modernen x86-Server dauert
cost=12etwa 250 ms pro Hash,cost=13rund 500 ms. - Zielen Sie auf 100–300 ms pro Verify auf Ihrer Produktionshardware. Messen statt raten.
- Denken Sie an die 72-Byte-Eingabegrenze. Wenn Nutzer Passphrasen wählen können, hashen Sie vorher mit SHA-256 (siehe FAQ).
bcrypts GPU-Resistenz ist durch seine 4-KiB-Speicherspur begrenzt. Kein bcrypt-Cost-Factor wird je die Speicherhärte von Argon2id erreichen. Nehmen Sie Argon2id, wenn Sie können.
Eine praktische Größenordnung: Auf einem EPYC-Server von 2024 läuft bcrypt(cost=12) in rund 250 ms, auf einem High-End-Laptop näher an 350 ms. Wenn Ihre Werte um eine Größenordnung außerhalb von 100–500 ms liegen, prüfen Sie, ob Ihre Bibliothek tatsächlich nativen bcrypt nutzt oder auf einen langsamen JavaScript-Polyfill zurückfällt (manche Bundler entfernen native Abhängigkeiten in Serverless-Builds).
PBKDF2: der Pfad zur FIPS-140-Konformität
PBKDF2 (RFC 8018) ist der Algorithmus letzter Wahl im Sicherheitsleitfaden. Er ist älter als bcrypt, nicht speicherhart und fällt GPU-Angriffen schneller zum Opfer als jeder der drei oben genannten. Aber er ist die einzige Passwort-Hashing-Primitive, die FIPS-140-validiert ist, was für Bundesbehörden, das US-Gesundheitsgesetz HIPAA und manche Finanzbereitstellungen relevant ist.
Wenn Sie PBKDF2 brauchen, nehmen Sie:
- HMAC-SHA-256 als PRF (kein SHA-1, kein reines SHA-256 ohne HMAC)
- mindestens 600.000 Iterationen (OWASP-2026-Basis)
- mindestens 16-byte zufälligen Salt pro Passwort
Wenn FIPS für Sie nicht gilt, nehmen Sie Argon2id. PBKDF2s Design mit fester Ausgabe und festem Speicher heißt: Jeder Dollar GPU-Silizium, den ein Angreifer kauft, übersetzt sich direkt in mehr Passwortversuche pro Sekunde.
NISTs SP 800-63B bezeichnet PBKDF2-HMAC als „approved” für Passwort-Hashing, geht aber nicht so weit, es gegenüber speicherharten Alternativen zu empfehlen. Lesen Sie das so: NIST erlaubt PBKDF2, weil seine Abschaffung jede Altinstallation der US-Regierung ungültig machen würde, nicht weil es die beste Wahl für ein neues Projekt ist.
Entscheidungsrahmen: Welchen Algorithmus sollten Sie wählen?
Vergleichstabelle
| Dimension | bcrypt | scrypt | Argon2id | PBKDF2 |
|---|---|---|---|---|
| Speicherhart | Nein | Ja | Ja | Nein |
| GPU-Resistenz | Mittel | Hoch | Sehr hoch | Niedrig |
| Seitenkanalresistenz | Mittel | Mittel | Hoch (id) | Mittel |
| Parameter-Komplexität | 1 (cost) | 3 (N, r, p) | 3 (m, t, p) | 1 (Iterationen) |
| Bibliotheksreife | Ausgezeichnet | Gut | Gut | Ausgezeichnet |
| Eingabe-Längengrenze | 72 bytes | Keine | Keine | Keine |
| Standardisierung | de facto | RFC 7914 | RFC 9106 | RFC 8018 |
| OWASP-2026-Status | Nur Legacy | Alternative | Erste Wahl | Nur FIPS |
Standardmäßig Argon2id
Für ein neues Projekt, also typische Web-App, moderner Node-/Python-/Go-/Rust-/JVM-Stack, kein FIPS-Zwang: nehmen Sie Argon2id mit m=19456, t=2, p=1. Sie bekommen die stärkste verfügbare GPU- und Seitenkanalresistenz, ein Format mit eingebetteten Parametern, das Bibliotheks-Upgrades übersteht, und keine Überraschungen bei Eingabelängen. Das Bibliotheks-Ökosystem ist reif: argon2 auf npm, argon2-cffi auf PyPI, golang.org/x/crypto/argon2, der argon2-Crate auf crates.io, alle gepflegt und benchmarkt.
Wann scrypt oder bcrypt stattdessen wählen
Nehmen Sie scrypt, wenn Argon2 in Ihrer Laufzeit nicht verfügbar ist (in 2026 wirklich selten, selbst Cloudflare Workers und Deno bieten ihn inzwischen) oder wenn Sie bereits ein scrypt-basiertes System produktiv betreiben und der Migrationsaufwand das Sicherheitsdelta überwiegt. scrypt ist nach wie vor ein solider Algorithmus; ihm fehlt nur der Seitenkanal-Feinschliff von Argon2id.
Nehmen Sie bcrypt, wenn Sie ein Altsystem pflegen, eine harte Anforderung zur Minimierung von Abhängigkeiten haben (kein nativer Code, keine zusätzlichen Pakete) und die 72-Byte-Eingabegrenze für Ihre Nutzerschaft akzeptabel ist. bcrypt läuft seit zwei Jahrzehnten im Internet-Maßstab; seine Fehlermodi sind gut verstanden.
Nehmen Sie PBKDF2, wenn der Regulator es vorgibt. Das ist der einzige Grund. Wenn Ihr Auditor Argon2id akzeptiert (was eine wachsende Zahl bei Nicht-FIPS-Workloads inzwischen tut), nehmen Sie Argon2id.
Häufige Fehler vermeiden
Die meisten Passwortspeicherungs-Pannen der letzten Dekade lassen sich auf eine kleine Menge wiederkehrender Engineering-Fehler zurückführen. Keiner davon ist exotisch; alle erwischen Sie, wenn Sie Ihren Auth-Code mit der folgenden Liste vor Augen durchgehen.
- Passwörter mit rohem SHA-256 oder MD5 hashen. Das ist der größte Einzelfehler bei der Passwortspeicherung. Siehe MD5 vs SHA-256 zu den Gründen, warum diese für Passwörter ungeeignet sind.
- Einen einzigen globalen Salt für alle Nutzer wiederverwenden. Ein Salt muss pro Datensatz eindeutig sein. Argon2 und bcrypt erzeugen ihn für Sie; setzen Sie das nicht außer Kraft.
- Hash-Zeit unter 50 ms. Sie haben Sicherheit gegen einen Geschwindigkeitsgewinn eingetauscht, den kein Nutzer wahrnehmen kann. Zielen Sie auf 100–500 ms.
- Hash-Zeit über 1 Sekunde. Sie haben einen Denial-of-Service-Vektor gegen Ihren eigenen Login-Endpoint geschaffen. Begrenzen Sie auf etwa 500 ms.
- Passwörter clientseitig hashen und das Digest an den Server senden. Der Hash ist jetzt das Passwort. Wer die Datenbank stiehlt, kann sich ohne Umkehr authentifizieren. Hashen Sie immer auf dem Server.
- Algorithmus-Parameter in einer separaten Spalte speichern. Das PHC-String-Format bettet sie in den Hash ein. Nutzen Sie das.
- Passwörter oder Hashes beim Error-Handling loggen. Beides gehört dem Nutzer, nicht Ihrem Log-Aggregator. Säubern Sie sie auf der Request-Parsing-Ebene, bevor sie irgendeinen Logger erreichen.
verify()-Exceptions wie Authentifizierungsfehler behandeln. Eine Bibliothek, die bei einem fehlerhaften gespeicherten Hash wirft, sollte den Fehler sichtbar machen, nicht stillschweigend zu „falsches Passwort” durchfallen. Unterscheiden Sie zwischen „falsches Passwort” (401) und „gespeicherter Hash ist defekt” (500 plus On-Call-Page).
Implementierung in der Praxis
Argon2id in Node.js
Das argon2-Paket (native Bindings zur Referenzimplementierung) ist die kanonische Wahl auf Node:
import argon2 from 'argon2';
// Hashen bei Registrierung oder Passwortwechsel
const hash = await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 19456, // 19 MiB
timeCost: 2,
parallelism: 1,
});
// → '$argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash>'
// Verifizieren beim Login
const ok = await argon2.verify(hash, candidate);
if (!ok) throw new Error('Invalid credentials');
// Veraltete Parameter erkennen und bei erfolgreichem Login neu hashen
if (argon2.needsRehash(hash, { type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1 })) {
const upgraded = await argon2.hash(candidate, {
type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1,
});
await db.users.update({ id: user.id }, { password_hash: upgraded });
}
Der needsRehash-Schritt ist das Geheimnis langfristiger Migration: Jeder erfolgreiche Login wird zur Gelegenheit, den gespeicherten Hash auf aktuelle Parameter zu heben, ohne den Nutzer zu behelligen.
Dasselbe Muster in Python mit argon2-cffi:
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher(memory_cost=19456, time_cost=2, parallelism=1)
# Hashen
stored = ph.hash(password)
# Verifizieren
try:
ph.verify(stored, candidate)
except VerifyMismatchError:
raise ValueError('Invalid credentials')
# Neu hashen bei Parameter-Upgrade
if ph.check_needs_rehash(stored):
stored = ph.hash(candidate)
In Go mit golang.org/x/crypto/argon2:
import (
"crypto/rand"
"golang.org/x/crypto/argon2"
)
func hashPassword(password string) ([]byte, []byte) {
salt := make([]byte, 16)
rand.Read(salt)
hash := argon2.IDKey([]byte(password), salt, 2, 19456, 1, 32)
return hash, salt
}
Die Go-Standardbibliothek liefert keinen PHC-Format-Encoder mit; wenn Sie die argon2.IDKey-Primitive direkt nutzen, sind Sie selbst dafür verantwortlich, Parameter und Salt neben dem Hash zu kodieren. Die meisten Go-Projekte nehmen dafür einen Wrapper wie github.com/alexedwards/argon2id.
Rust mit dem argon2-Crate ist ähnlich idiomatisch:
use argon2::{Argon2, PasswordHasher, PasswordVerifier, password_hash::{SaltString, rand_core::OsRng}};
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default(); // Argon2id, m=19456, t=2, p=1 als Default
let hash = argon2.hash_password(password.as_bytes(), &salt)?.to_string();
// Beim Verify
let parsed = argon2::password_hash::PasswordHash::new(&hash)?;
argon2.verify_password(candidate.as_bytes(), &parsed)?;
In allen drei Laufzeiten ist die erzeugte Zeichenkette austauschbar: Ein in Node erstellter Hash verifiziert sauber in Python oder Rust. Diese laufzeitübergreifende Kompatibilität macht Argon2 für polyglotte Architekturen zur sichereren Wahl als algorithmusspezifische Wrapper.
Migrationsmuster bcrypt zu Argon2id
Die User-Tabelle einfach leeren und neu anfangen — diese Option haben Sie fast nie. Das richtige Migrationsmuster ist dasselbe, das im MD5-zu-bcrypt-Abschnitt der FAQ unseres Hash-Generators steht: ein sanftes, login-getriebenes Upgrade.
Fügen Sie eine Spalte hinzu, die den Algorithmus markiert:
ALTER TABLE users ADD COLUMN password_algo VARCHAR(16) NOT NULL DEFAULT 'bcrypt';
Beim Login an den richtigen Verifier weiterreichen:
async function verifyAndMaybeRehash(user, candidate) {
let ok;
if (user.password_algo === 'argon2id') {
ok = await argon2.verify(user.password_hash, candidate);
} else if (user.password_algo === 'bcrypt') {
ok = await bcrypt.compare(candidate, user.password_hash);
if (ok) {
// Erfolgreicher Legacy-Verify → mit Argon2id neu hashen
const newHash = await argon2.hash(candidate, {
type: argon2.argon2id, memoryCost: 19456, timeCost: 2, parallelism: 1,
});
await db.users.update({ id: user.id }, {
password_hash: newHash,
password_algo: 'argon2id',
});
}
}
return ok;
}
Setzen Sie ein Auslauffenster von 6–12 Monaten. Verschicken Sie nach 9 Monaten eine E-Mail im Stil „Ihr Passwort wird nach einem veralteten Verfahren gespeichert, bitte loggen Sie sich zum Upgrade ein”. Nach 12 Monaten erzwingen Konten, die immer noch auf bcrypt liegen, beim nächsten Login einen Passwort-Reset. Aktive Nutzer migrieren transparent; inaktive Konten bekommen ein einmaliges Reibungsereignis.
Dasselbe Muster funktioniert für die Migration weg von scrypt oder PBKDF2. Der einzige Zustand, den Sie brauchen, ist die Spalte password_algo.
Pepper, Längengrenzen und Encoding-Fallstricke
Ein paar scharfe Kanten, die echte Deployments verletzen:
Pepper. Ein Pepper ist ein anwendungsweites Geheimnis, das vor dem Hashen zu jedem Passwort hinzugefügt und getrennt von der Datenbank gespeichert wird (in einem KMS, einer Umgebungsvariable oder in HashiCorp Vault). Wenn Ihre Datenbank leakt, Ihr App-Geheimnis aber nicht, sind die geleakten Hashes ohne den Pepper nicht angreifbar. Wenden Sie ihn als HMAC an, nicht als Konkatenation:
import { createHmac } from 'crypto';
const peppered = createHmac('sha256', process.env.PEPPER).update(password).digest();
const hash = await argon2.hash(peppered, { type: argon2.argon2id, /* ... */ });
Rotieren Sie den Pepper selten (es erfordert Re-Hashing), unterstützen Sie Rotation aber durch Versionierung: PEPPER_V2, mit Fallback auf PEPPER_V1 beim Verify.
bcrypts 72-Byte-Grenze. Wenn Sie bcrypt nutzen müssen und beliebig lange Passwörter unterstützen wollen, hashen Sie vorher mit SHA-256 und kodieren Sie base64 (so umgehen Sie eingebettete NUL-bytes, die bcrypt ebenfalls inkonsistent behandelt):
import { createHash } from 'crypto';
const prepped = createHash('sha256').update(password, 'utf8').digest('base64');
const hash = await bcrypt.hash(prepped, 12);
Dieselbe prepped-Transformation muss beim Verify laufen. Dokumentieren Sie das in Ihrem Auth-Code mit einem riesigen Kommentar; Ihr zukünftiges Ich wird es Ihrem heutigen Ich danken.
UTF-8-Normalisierung. Die Zeichenkette "café" lässt sich entweder als c-a-f-é (4 Codepunkte, NFC) oder als c-a-f-e + kombinierender Akut (5 Codepunkte, NFD) kodieren. Sie sehen identisch aus, erzeugen aber unterschiedliche Hashes. Normalisieren Sie vor dem Hashen immer auf NFC:
const normalized = password.normalize('NFC');
Das beißt bei mobilen Tastaturen und beim Copy-Paste aus PDFs öfter, als man denkt.
Niemals clientseitig vor-hashen. Ein clientseitig berechneter Hash, der an den Server gesendet wird, ist das neue Passwort. Wer Ihre Datenbank liest, kann sich authentifizieren. Hashen Sie auf dem Server, Punkt. JWTs ändern daran nichts: siehe JWT Token dekodieren dazu, was JWTs authentifizieren und was nicht.
Auf Produktionshardware benchmarken, nicht auf Ihrem Laptop. Ein Intel-Laptop der 13. Generation, der Argon2id mit m=19456, t=2, p=1 ausführt, ist in rund 35 ms fertig. Dieselben Parameter brauchen auf einer t3.small-EC2-Instanz eher 180 ms, auf einem Raspberry Pi 4 über 600 ms. Nehmen Sie die Hardware, auf der das Produkt tatsächlich läuft, messen Sie 1.000 Verifies und tunen Sie auf den Median. Login-Latenzschwankungen aus Cold-Start-Serverless-Containern sind ebenfalls eine Messung wert: Lambda-Cold-Starts können 200–800 ms hinzufügen, die nichts mit dem Hashen zu tun haben.
FAQ
Was ist der Unterschied zwischen Passwort-Hashing und Verschlüsselung?
Hashen ist einseitig: Sie berechnen einen Fingerabdruck fester Länge, der nicht auf die Eingabe rückführbar ist. Verschlüsselung ist zweiseitig: Mit dem richtigen Schlüssel können Sie zurück ins Original entschlüsseln. Passwörter müssen gehasht, nicht verschlüsselt werden. Ein Server soll das Passwort eines Nutzers nicht wiederherstellen können — so wird aus einem Datenbankleak kein Credential-Leak.
Warum kann ich für Passwörter nicht einfach SHA-256 nehmen?
SHA-256 ist auf Geschwindigkeit ausgelegt. Eine moderne GPU berechnet 22 Milliarden SHA-256-Hashes pro Sekunde, also fällt ein achtstelliges Kleinbuchstaben-Passwort aus einer geleakten Datenbank in Minuten. Passwort-Hashes brauchen drei Eigenschaften, die SHA-256 fehlen: bewusst langsame Ausführung, Salt pro Datensatz und Speicherhärte. Das Tradeoff-Prinzip ist dasselbe wie im Hinweis „Don’t Use MD5 for Security” unseres MD5 & SHA-256 Hash-Generators, und Sie können mehr darüber lesen, wie Angreifer schwache Hashes in Klartext verwandeln, in Passwort-Entropie.
Ist bcrypt 2026 noch sicher?
bcrypt selbst wurde nicht gebrochen. Der Blowfish-basierte Schlüsselplan bleibt kryptografisch solide. Was sich geändert hat, ist das Bedrohungsmodell: GPUs und ASICs machen die fehlende Speicherhärte von bcrypt zu einer messbaren Schwäche gegenüber Argon2id. OWASPs Position 2026 lautet: bcrypt ist für Altsysteme mit cost ≥ 10 akzeptabel, aber neue Projekte sollten Argon2id nehmen.
Argon2i vs Argon2d vs Argon2id — welchen soll ich nehmen?
Nehmen Sie Argon2id. RFC 9106 schreibt ihn als empfohlene Variante für Passwort-Hashing fest. Argon2i ist datenunabhängig (seitenkanalsicher, aber schwächer gegen GPU-Tradeoff-Angriffe). Argon2d ist datenabhängig (GPU-stark, aber anfällig für Cache-Timing-Seitenkanäle). Argon2id ist ein Hybrid, der beide Eigenschaften zum Preis von einem liefert.
Wie wähle ich Argon2id-Parameter für meine App?
Starten Sie mit der OWASP-Basis: m=19456, t=2, p=1. Dann auf Ihrer Produktions-CPU benchmarken und anpassen:
- Legen Sie Ihr RAM-Budget pro Login fest (z. B. 50 MiB bei Spitzenparallelität).
- Setzen Sie
mauf diesen Wert oder darunter. - Lassen Sie
argon2.hash()in einer Schleife laufen und messen Sie die Wandzeit. - Erhöhen Sie
t, bis der Median zwischen 100 und 500 ms liegt.
Lassen Sie p=1, sofern Sie nicht profiliert haben und wissen, dass Multi-Lane-Parallelität in Ihrer Laufzeit hilft. Bei Auth-Servern mit hohem Traffic ergibt eine Verschiebung zu höherem t und niedrigerem m oft mehr RAM-Spielraum.
Was ist die 72-Byte-Grenze von bcrypt und wie behandle ich lange Passphrasen?
bcrypt füttert seine Eingabe in den Blowfish-Schlüsselplan, der bei 72 bytes abschneidet. Eine 150-stellige Passphrase hat dieselbe Sicherheit wie ihre ersten 72 bytes; der Rest wird ignoriert. Die Lösung: vorher mit SHA-256 (32 bytes) oder SHA-512 (64 bytes) hashen, das Digest base64-kodieren (um NUL-bytes zu vermeiden) und das an bcrypt übergeben. Argon2id und scrypt haben diese Grenze nicht; sie nehmen beliebig lange Eingaben direkt entgegen.
Kann ich von bcrypt zu Argon2 migrieren, ohne Passwort-Resets zu erzwingen?
Ja. Das Muster: beide Algorithmen hinter einer Spalte password_algo speichern, die Verifikation an die richtige Bibliothek dispatchen und bei jedem erfolgreichen bcrypt-Verify sofort mit Argon2id neu hashen und die Zeile aktualisieren. Aktive Nutzer migrieren still im Rahmen ihres normalen Login-Rhythmus. Setzen Sie ein Auslauffenster von 6–12 Monaten für inaktive Konten und erzwingen Sie danach einen Passwort-Reset für jeden Datensatz, der noch auf bcrypt liegt. Dasselbe Muster funktioniert für jede Algorithmus-zu-Algorithmus-Migration.
Ist PBKDF2 2026 noch eine gute Wahl?
Nur wenn die FIPS-140-Konformität es erzwingt, also typisch in Bundesbehörden, im regulierten Gesundheitswesen (HIPAA) und in bestimmten Finanzsystemen. Verwenden Sie HMAC-SHA-256 als PRF mit mindestens 600.000 Iterationen. PBKDF2 ist nicht speicherhart und fällt GPU-Angriffen daher schneller zum Opfer als Argon2id bei gleichem Latenzbudget. Wenn FIPS für Sie nicht gilt, nehmen Sie Argon2id und sparen sich die Compliance-Akrobatik.
Die Antwort zum Passwort-Hashing 2026 ist kurz: Standard ist Argon2id mit OWASPs Basisparametern, Fallback ist scrypt, falls Argon2 nicht verfügbar ist, bcrypt nur dort, wo Legacy es verlangt, und PBKDF2 reserviert für FIPS-gebundene Systeme. Kombinieren Sie den Hash mit einem Salt pro Datensatz (jede moderne Bibliothek erledigt das automatisch), einem Pepper auf Anwendungsebene, der außerhalb der Datenbank gespeichert ist, und einer login-getriebenen Re-Hash-Schleife, mit der Sie Arbeitsfaktoren parallel zur Hardware-Entwicklung anheben können.
Erzeugen Sie ein repräsentatives Passwort-Set mit dem Zufallspasswort-Generator, benchmarken Sie Ihren Verify-Pfad gegen Ihre Produktions-CPU und schreiben Sie die Parameter in eine Konstanten-Datei, damit der nächste Engineer 2028 genau weiß, was hochzudrehen ist. Den vollen Sicherheitskontext (TLS, Session-Management, Rate-Limiting, MFA) finden Sie im Leitfaden zu Web-Sicherheit. Wählen Sie heute den richtigen Hash für Ihre App.