Von password_hash() bis zum sicheren Rehashing-Workflow
Passwort-Hashing entscheidet direkt darueber, wie teuer ein Angreifer nach einem Datenbank-Leak fuer jedes einzelne geknackte Passwort bezahlen muss. Argon2id gilt heute als bevorzugter Algorithmus, weil es speicherintensiv und damit resistent gegen GPU- und ASIC-gestuetzte Angriffe ist, waehrend bcrypt mit seiner CPU-gebundenen Kostenfunktion seit Jahrzehnten als solider Standard funktioniert. Dieser Artikel zeigt, wie beide Algorithmen ueber die PHP-Standard-API password_hash() und password_verify() korrekt konfiguriert, gesalzen, gepeppert und im laufenden Betrieb migriert werden.
Inhaltsverzeichnis
- 1. Warum Passwort-Hashing kein Verschluesseln ist
- 2. password_hash() und password_verify() als PHP-Standard-API
- 3. bcrypt im Detail: Cost-Faktor und die 72-Byte-Grenze
- 4. Argon2i vs. Argon2id: Unterschiede und Empfehlung
- 5. Argon2-Parameter richtig konfigurieren
- 6. Salting: automatisch, aber wichtig zu verstehen
- 7. Pepper als zusaetzliche Verteidigungsschicht
- 8. Migration und Rehashing mit password_needs_rehash()
- 9. Argon2 vs. bcrypt im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Passwort-Hashing kein Verschluesseln ist
Ein Passwort zu verschluesseln bedeutet, es mit einem Schluessel reversibel umzuwandeln: Wer den Schluessel besitzt, kann den Klartext jederzeit zurueckgewinnen. Beim Passwort-Hashing gilt genau das Gegenteil. Eine kryptographische Hashfunktion bildet eine Eingabe beliebiger Laenge auf einen Ausgabewert fester Laenge ab, und dieser Prozess ist absichtlich nicht umkehrbar. Es gibt keinen Schluessel, den man verlieren, stehlen oder falsch verwahren koennte, weil es schlicht keinen Weg zurueck vom Hash zum Klartext gibt. Genau diese Einwegeigenschaft ist der Grund, warum seriöse Systeme Passwoerter niemals verschluesseln, sondern ausschliesslich hashen.
Die praktische Konsequenz betrifft den gesamten Authentifizierungs-Workflow: Bei der Registrierung wird das Passwort einmal gehasht und nur der Hash gespeichert, das Klartext-Passwort existiert danach nirgendwo mehr im System. Beim Login wird das eingegebene Passwort erneut gehasht und der neue Hash mit dem gespeicherten Hash verglichen, niemals wird ein gespeicherter Wert entschluesselt. Wenn eine Anwendung in der Lage ist, einem Nutzer sein bestehendes Passwort per E-Mail zuzuschicken oder es im Klartext im Admin-Bereich anzuzeigen, ist das ein zuverlaessiges Warnsignal dafuer, dass irgendwo im System reversible Verschluesselung statt echtem Passwort-Hashing verwendet wird, und das ist unabhaengig vom eingesetzten Framework immer ein Sicherheitsmangel.
2. password_hash() und password_verify() als PHP-Standard-API
PHP liefert mit password_hash() und password_verify() seit Version 5.5 eine vollstaendige, auditierte Standard-API fuer Passwort-Hashing mit. Wer stattdessen md5(), sha1() oder einen einfachen hash('sha256', $passwort)-Aufruf verwendet, macht sich selbst fuer Salting, Cost-Parameter und die Wahl eines geeigneten Algorithmus verantwortlich, und Erfahrung aus unzaehligen Sicherheitsaudits zeigt, dass genau das regelmaessig schiefgeht. Diese Hashfunktionen wurden fuer Geschwindigkeit optimiert, was bei einer Pruefsumme erwuenscht ist, bei Passwoertern aber genau das Gegenteil von dem, was man braucht: Ein schneller Hash laesst sich milliardenfach pro Sekunde auf spezialisierter Hardware durchprobieren.
Die Konstante PASSWORD_DEFAULT zeigt aktuell auf bcrypt, weil dieser Algorithmus historisch zuerst als Standard in PHP verfuegbar war, sie kann sich aber mit zukuenftigen PHP-Versionen aendern, sobald ein staerkerer Algorithmus zum neuen Standard wird. Wer PASSWORD_DEFAULT verwendet, profitiert automatisch von solchen Verbesserungen, muss dafuer aber akzeptieren, dass sich die Laenge und das Format des erzeugten Hashes aendern kann, weshalb das Datenbankfeld fuer den Hash grosszuegig dimensioniert werden sollte, etwa als VARCHAR(255) statt eines knapp bemessenen festen Feldes.
Ein zentraler Vorteil dieser API ist, dass der zurueckgegebene String bereits alle Informationen enthaelt, die spaeter fuer die Verifikation gebraucht werden: Algorithmus-Kennung, Cost-Parameter und Salt sind direkt im Hash-String kodiert. Entwickler muessen Salt und Parameter nirgendwo separat verwalten, password_verify() liest diese Metadaten aus dem gespeicherten Hash selbst aus und rekonstruiert daraus die exakt gleichen Parameter fuer den Vergleich. Der folgende Code zeigt den kompletten Registrierungs- und Login-Ablauf mit dieser API.
<?php
declare(strict_types=1);
/**
* Registration: hash the password once, store only the hash string.
* PASSWORD_DEFAULT currently maps to bcrypt but may change in future PHP releases.
*/
function registerUser(string $email, string $plainPassword, PDO $pdo): void
{
$hash = password_hash($plainPassword, PASSWORD_DEFAULT);
if ($hash === false) {
throw new RuntimeException('Password hashing failed, check available algorithms.');
}
$stmt = $pdo->prepare(
'INSERT INTO users (email, password_hash) VALUES (:email, :hash)'
);
$stmt->execute(['email' => $email, 'hash' => $hash]);
}
/**
* Login: hash the submitted password again and compare it
* against the stored hash using a timing-attack-safe comparison.
*/
function verifyLogin(string $email, string $plainPassword, PDO $pdo): bool
{
$stmt = $pdo->prepare('SELECT password_hash FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
$storedHash = $stmt->fetchColumn();
if ($storedHash === false) {
// Run verify against a dummy hash anyway to avoid leaking
// via response-time whether the email exists at all.
password_verify($plainPassword, '$2y$12$UnknownUnknownUnknownUuU');
return false;
}
// password_verify() itself is constant-time internally.
return password_verify($plainPassword, $storedHash);
}
3. bcrypt im Detail: Cost-Faktor und die 72-Byte-Grenze
PASSWORD_BCRYPT implementiert den Blowfish-basierten bcrypt-Algorithmus mit einem konfigurierbaren cost-Parameter, der die Anzahl der internen Runden als Zweierpotenz festlegt. Ein Anstieg des Cost-Wertes um eins verdoppelt die benoetigte Rechenzeit, weil die Anzahl der Runden exponentiell mit dem Parameter waechst. Der Standardwert liegt bei 10, in der Praxis empfiehlt sich fuer aktuelle Server-Hardware meist ein Wert zwischen 12 und 14, sodass ein einzelner Hash-Vorgang irgendwo zwischen 100 und 300 Millisekunden dauert und damit fuer legitime Logins kaum spuerbar, fuer Brute-Force-Versuche in grossem Massstab aber empfindlich teuer ist.
Eine oft uebersehene Eigenschaft von bcrypt ist die harte Grenze von 72 Byte fuer die Eingabe. Alles, was ueber diese Grenze hinausgeht, wird von der zugrunde liegenden Implementierung stillschweigend abgeschnitten, ohne Fehler oder Warnung. Das bedeutet konkret: Zwei Passphrasen, die sich erst nach dem 72. Byte unterscheiden, erzeugen denselben bcrypt-Hash und werden von password_verify() beide als gueltig akzeptiert. Bei sehr langen Passphrasen, wie sie von Passwort-Managern oder bewusst langen Merksaetzen erzeugt werden, kann dieses Verhalten die tatsaechliche Sicherheit unbemerkt untergraben, weil ein Teil der eingegebenen Entropie schlicht nie in den Hash einfliesst.
Die uebliche Mitigation ist ein Pre-Hashing der Eingabe mit einer kryptographischen Hashfunktion fester Ausgabelaenge, bevor sie an password_hash() uebergeben wird. Ein Base64- oder Hex-kodierter SHA-384-Hash liegt sicher unter der 72-Byte-Grenze, unabhaengig davon, wie lang das urspruengliche Passwort war, und die volle Entropie der Eingabe fliesst dadurch garantiert in den bcrypt-Hash ein. Bei Argon2id besteht dieses Problem praktisch nicht, weil dort intern zunaechst ebenfalls eine Hashfunktion auf die Eingabe angewendet wird und keine vergleichbare kurze harte Grenze existiert.
<?php
declare(strict_types=1);
/**
* bcrypt with an explicit cost factor. Cost 12 is a reasonable
* baseline for current server hardware (roughly 100-300ms per hash).
*/
$options = ['cost' => 12];
$hash = password_hash($plainPassword, PASSWORD_BCRYPT, $options);
/**
* Demonstration of the 72-byte truncation problem: these two
* passphrases only differ after byte 72 and therefore hash identically.
*/
$passphraseA = str_repeat('a', 72) . 'first-suffix';
$passphraseB = str_repeat('a', 72) . 'second-suffix';
$hashA = password_hash($passphraseA, PASSWORD_BCRYPT);
var_dump(password_verify($passphraseB, $hashA)); // true - silently truncated!
/**
* Mitigation: pre-hash long passphrases with a fixed-length digest
* before handing them to password_hash(). This preserves full entropy.
*/
function safeBcryptHash(string $plainPassword): string
{
$preHashed = hash('sha384', $plainPassword, true);
return password_hash(base64_encode($preHashed), PASSWORD_BCRYPT, ['cost' => 12]);
}
4. Argon2i vs. Argon2id: Unterschiede und Empfehlung
Argon2 gewann 2015 die Password Hashing Competition und existiert in PHP in zwei nutzbaren Varianten. PASSWORD_ARGON2I wurde speziell dafuer optimiert, Seitenkanal-Angriffe zu erschweren, also Angriffe, bei denen ein Angreifer aus Speicherzugriffsmustern oder Cache-Timing Rueckschluesse auf die verarbeiteten Daten zieht. Diese Eigenschaft macht Argon2i in Umgebungen relevant, in denen ein Angreifer moeglicherweise Zugriff auf denselben physischen Host hat, etwa in geteilten Cloud-Umgebungen, aber sie allein reicht fuer moderne Bedrohungsmodelle nicht mehr aus.
PASSWORD_ARGON2ID kombiniert die Seitenkanal-Resistenz von Argon2i mit der Widerstandsfaehigkeit von Argon2d gegen GPU- und ASIC-gestuetzte Brute-Force-Angriffe, indem es beide internen Zugriffsmuster in unterschiedlichen Phasen der Berechnung kombiniert. Diese Hybrid-Konstruktion macht Argon2id fuer nahezu jeden praktischen Anwendungsfall zur richtigen Wahl, weil sie die Vorteile beider Vorgaengervarianten vereint, ohne einen der beiden Angriffsvektoren ungeschuetzt zu lassen. Aus diesem Grund empfiehlt die IETF Argon2id explizit als Standardvariante fuer Passwort-Hashing, und PASSWORD_ARGON2ID sollte in neuen PHP-Projekten der bevorzugte Algorithmus sein, sobald die Zielumgebung ihn unterstuetzt.
5. Argon2-Parameter richtig konfigurieren
Argon2id nimmt in PHP drei konfigurierbare Parameter entgegen: memory_cost in Kibibyte, time_cost als Anzahl der Iterationen und threads als Grad der Parallelisierung. Der memory_cost-Parameter ist der entscheidende Unterschied zu bcrypt, weil er den Algorithmus speicherintensiv macht: Ein Angreifer, der viele Hashes gleichzeitig auf spezialisierter Hardware knacken will, benoetigt fuer jede parallele Berechnung den vollen konfigurierten Speicherbetrag, was Massenangriffe auf GPUs mit begrenztem Speicher pro Recheneinheit deutlich verteuert.
Die PHP-Standardwerte, PASSWORD_ARGON2_DEFAULT_MEMORY_COST mit 65536 KiB (also 64 MiB), PASSWORD_ARGON2_DEFAULT_TIME_COST mit 4 und PASSWORD_ARGON2_DEFAULT_THREADS mit 1, sind ein vernuenftiger Ausgangspunkt, aber jede Produktionsumgebung sollte sie an die tatsaechliche Serverhardware anpassen. Ein Applikationsserver mit begrenztem RAM unter hoher gleichzeitiger Login-Last kann bei zu hohem memory_cost selbst zum Flaschenhals werden, waehrend ein zu niedriger Wert die eigentliche Schutzwirkung von Argon2id wieder aufhebt.
Der pragmatische Weg zur richtigen Konfiguration ist Benchmarking auf der Zielhardware: Man erhoeht memory_cost und time_cost schrittweise und misst die tatsaechliche Hashing-Dauer, bis ein Zielwert von etwa 250 bis 500 Millisekunden pro Hash-Vorgang erreicht ist, ein Wert, der bei legitimen Logins kaum auffaellt, Brute-Force-Versuche aber massiv verlangsamt. Dieser Benchmark sollte unter realistischer Last wiederholt werden, weil parallele Logins mit knappem Server-RAM zu ungewuenschtem Swapping fuehren koennen, wenn memory_cost zu grosszuegig gewaehlt wurde.
<?php
declare(strict_types=1);
/**
* Argon2id with explicit tuning parameters.
* memory_cost is given in KiB: 65536 = 64 MiB per hashing operation.
*/
$options = [
'memory_cost' => 65536, // 64 MiB - raise for stronger memory-hardness
'time_cost' => 4, // number of iterations
'threads' => 2, // parallel lanes, tune to available CPU cores
];
$hash = password_hash($plainPassword, PASSWORD_ARGON2ID, $options);
/**
* Simple benchmark loop to find parameters that hash in ~300ms
* on the actual production hardware.
*/
function benchmarkArgon2id(string $sample, int $memoryCost, int $timeCost): float
{
$start = hrtime(true);
password_hash($sample, PASSWORD_ARGON2ID, [
'memory_cost' => $memoryCost,
'time_cost' => $timeCost,
'threads' => 2,
]);
return (hrtime(true) - $start) / 1_000_000; // milliseconds
}
6. Salting: automatisch, aber wichtig zu verstehen
Ein Salt ist ein zufaelliger Wert, der vor dem Hashen mit dem Passwort kombiniert wird, und sein wichtigster Zweck ist es, vorab berechnete Rainbow-Tables wirkungslos zu machen. Ohne Salt wuerden zwei Nutzer mit demselben Passwort denselben Hash erzeugen, und ein Angreifer koennte eine einzige Tabelle mit vorberechneten Hashes gegen die gesamte Datenbank abgleichen. Mit einem einzigartigen Salt pro Passwort muss ein Angreifer fuer jeden einzelnen Hash eine eigene Berechnung durchfuehren, selbst wenn zwei Nutzer zufaellig identische Passwoerter gewaehlt haben.
password_hash() erzeugt bei jedem Aufruf automatisch einen kryptographisch sicheren, zufaelligen Salt ueber den Zufallszahlengenerator des Betriebssystems und kodiert ihn direkt in den zurueckgegebenen Hash-String, sodass Entwickler sich um Erzeugung, Speicherung oder Uebergabe des Salts nicht selbst kuemmern muessen. Wichtig ist trotzdem zu verstehen, warum das so ist: Ein statischer, fest im Code hinterlegter Salt fuer alle Passwoerter waere fast so schwach wie gar kein Salt, weil ein Angreifer dann wieder eine einzige Rainbow-Table fuer genau diesen einen Salt-Wert vorberechnen koennte. Die in aelteren, selbstgebauten Loesungen manchmal anzutreffende Praxis, einen globalen Salt aus einer Konfigurationsdatei zu verwenden, sollte deshalb konsequent vermieden werden.
7. Pepper als zusaetzliche Verteidigungsschicht
Waehrend der Salt pro Passwort einzigartig und im Hash sichtbar gespeichert wird, ist ein Pepper ein einziger, geheimer Wert, der fuer die gesamte Anwendung gilt und ausserhalb der Datenbank aufbewahrt wird, etwa als Umgebungsvariable, in einem Secrets-Manager oder in einem Hardware-Sicherheitsmodul. Der Pepper wird vor dem eigentlichen password_hash()-Aufruf mit dem Passwort kombiniert, sodass er in keinem der gespeicherten Hash-Werte auftaucht und aus einem reinen Datenbank-Dump nicht rekonstruierbar ist.
Der praktische Nutzen zeigt sich in einem konkreten Bedrohungsszenario: Wird die Datenbank durch eine SQL-Injection oder einen Backup-Leak exfiltriert, aber der Anwendungscode und die Umgebungsvariablen bleiben unberuehrt, kann ein Angreifer ohne den Pepper trotz vollstaendiger Hash-Tabelle keine gueltigen Offline-Brute-Force-Angriffe gegen die Passwoerter fahren. Diese zusaetzliche Trennung von Datenbank-Kompromittierung und Anwendungs-Kompromittierung ist der zentrale Mehrwert eines Peppers gegenueber reinem Salting.
Technisch wird der Pepper meist nicht direkt an das Passwort angehaengt, sondern per hash_hmac() mit einem starken Algorithmus wie SHA-256 kombiniert, bevor das Ergebnis an password_hash() uebergeben wird. Dieses Vorgehen vermeidet Laengenprobleme bei der Kombination und stellt sicher, dass die Ausgabe unabhaengig von der Laenge des urspruenglichen Passworts eine feste, sichere Struktur hat.
<?php
declare(strict_types=1);
/**
* Pepper: application-wide secret, loaded from environment,
* never stored in the database alongside the hash.
*/
function pepperedInput(string $plainPassword): string
{
$pepper = getenv('PASSWORD_PEPPER');
if ($pepper === false || $pepper === '') {
throw new RuntimeException('PASSWORD_PEPPER environment variable is not set.');
}
// Combine password and pepper via HMAC before hashing.
return hash_hmac('sha256', $plainPassword, $pepper);
}
function hashWithPepper(string $plainPassword): string
{
return password_hash(pepperedInput($plainPassword), PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 2,
]);
}
function verifyWithPepper(string $plainPassword, string $storedHash): bool
{
return password_verify(pepperedInput($plainPassword), $storedHash);
}
8. Migration und Rehashing mit password_needs_rehash()
Sicherheitsanforderungen aendern sich mit der Zeit: Ein Cost-Faktor, der vor fuenf Jahren angemessen war, kann heute zu schwach sein, und ein Wechsel von bcrypt zu Argon2id erfordert eine Strategie, um bestehende Nutzerkonten ohne erzwungenen Passwort-Reset zu migrieren. password_needs_rehash() loest genau dieses Problem: Die Funktion prueft, ob ein gespeicherter Hash mit dem aktuell gewuenschten Algorithmus und den aktuell gewuenschten Parametern erzeugt wurde, und gibt true zurueck, wenn das nicht der Fall ist.
Der uebliche Ort fuer diese Pruefung ist unmittelbar nach einem erfolgreichen Login, denn genau in diesem Moment liegt das Passwort im Klartext vor, was fuer das Erzeugen eines neuen Hashes zwingend erforderlich ist. Schlaegt password_needs_rehash() vor, den Hash zu erneuern, wird transparent im Hintergrund ein neuer Hash mit den aktuellen Zielparametern erzeugt und der alte Wert in der Datenbank ersetzt, ohne dass der Nutzer davon etwas mitbekommt oder sein Passwort erneut eingeben muss. Ueber viele Logins hinweg migrieren sich so nach und nach alle aktiven Konten automatisch auf die neuen Parameter, waehrend inaktive Konten erst beim naechsten Login aktualisiert werden.
Fuer die Migration von wirklich veralteten Hash-Formaten wie md5 oder sha1 aus Legacy-Systemen laesst sich derselbe Mechanismus erweitern: Man erkennt am Format des gespeicherten Werts, etwa an dessen Laenge oder daran, dass er nicht mit $2y$ oder $argon2id$ beginnt, dass es sich um einen Alt-Hash handelt, prueft das eingegebene Passwort gegen diesen Alt-Hash mit der alten Methode und erzeugt bei Erfolg sofort einen neuen, mit password_hash() erstellten Hash. So laesst sich ein komplettes Altsystem schrittweise und ohne Zwangs-Reset auf die moderne API umstellen.
<?php
declare(strict_types=1);
/**
* Called after a successful login. Transparently upgrades the
* stored hash if it uses outdated algorithm or cost parameters.
*/
function loginAndMaybeRehash(string $email, string $plainPassword, PDO $pdo): bool
{
$stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row === false) {
return false;
}
$storedHash = $row['password_hash'];
$legacyFormat = !str_starts_with($storedHash, '$2y$')
&& !str_starts_with($storedHash, '$argon2id$');
// Legacy migration path: verify against md5, then upgrade on success.
if ($legacyFormat) {
if (!hash_equals($storedHash, md5($plainPassword))) {
return false;
}
} elseif (!password_verify($plainPassword, $storedHash)) {
return false;
}
$targetOptions = ['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 2];
if ($legacyFormat || password_needs_rehash($storedHash, PASSWORD_ARGON2ID, $targetOptions)) {
$newHash = password_hash($plainPassword, PASSWORD_ARGON2ID, $targetOptions);
$update = $pdo->prepare('UPDATE users SET password_hash = :hash WHERE id = :id');
$update->execute(['hash' => $newHash, 'id' => $row['id']]);
}
return true;
}
9. Argon2 vs. bcrypt im direkten Vergleich
Beide Algorithmen sind fuer Passwort-Hashing geeignet und beide werden von der PHP-Standard-API unterstuetzt, unterscheiden sich aber in Ressourcenprofil, konfigurierbaren Parametern und Widerstandsfaehigkeit gegen spezialisierte Angriffshardware. Die folgende Tabelle fasst die wichtigsten Unterschiede zusammen und gibt eine konkrete Empfehlung fuer jedes Kriterium.
| Kriterium | bcrypt | Argon2id | Empfehlung |
|---|---|---|---|
| Max. Eingabelaenge | 72 Byte, stille Kuerzung | praktisch unbegrenzt | Bei bcrypt lange Passphrasen vorab hashen |
| Ressourcenprofil | rein CPU-gebunden | memory-hard (CPU + RAM) | Argon2id erschwert Massen-Hardware-Angriffe |
| Seitenkanal- / GPU-Resistenz | mittel | hoch | Argon2id fuer neue Systeme bevorzugen |
| Konfigurierbare Parameter | nur cost | memory_cost, time_cost, threads | Argon2id bietet feineres Tuning |
| PHP-Standardverfuegbarkeit | seit PHP 5.5, immer verfuegbar | seit PHP 7.3, benoetigt libargon2 | Verfuegbarkeit vorab mit password_algos() pruefen |
Fuer neue Projekte auf aktueller Infrastruktur ist PASSWORD_ARGON2ID die technisch ueberlegene Wahl, weil sein Speicherbedarf spezialisierte Angriffshardware deutlich verteuert. Wo Argon2id aus Kompatibilitaetsgruenden nicht verfuegbar ist, etwa auf Legacy-Hosting ohne libargon2-Unterstuetzung, bleibt bcrypt mit einem angemessenen Cost-Faktor und korrektem Umgang mit der 72-Byte-Grenze weiterhin eine solide und ausreichend sichere Option fuer Passwort-Hashing.
10. Zusammenfassung
Sicheres Passwort-Hashing beginnt mit der Erkenntnis, dass Passwoerter niemals verschluesselt, sondern immer gehasht werden. Die PHP-Standard-API password_hash() und password_verify() nimmt Entwicklern die fehleranfaellige Detailarbeit rund um Salt-Erzeugung, Algorithmus-Kodierung und sicheren Vergleich ab. Zwischen den beiden verfuegbaren Algorithmen ist Argon2id dank seines speicherintensiven Designs die robustere Wahl gegen moderne, hardwaregestuetzte Angriffe, waehrend bcrypt mit korrekt gesetztem Cost-Faktor und einer Absicherung gegen die 72-Byte-Grenze weiterhin praxistauglich bleibt.
Ein Pepper als zusaetzliche, ausserhalb der Datenbank gespeicherte Geheimschicht erhoeht die Sicherheit im Fall eines reinen Datenbank-Leaks erheblich, und password_needs_rehash() stellt sicher, dass bestehende Konten ohne erzwungenen Reset schrittweise auf staerkere Parameter migrieren. Wer diese Bausteine, Algorithmus-Wahl, Cost-Tuning, Salting, Peppering und Rehashing konsequent kombiniert, betreibt ein Authentifizierungssystem, das auch nach einem Datenbank-Kompromiss den Grossteil der Passwoerter wirksam schuetzt.
Passwort-Hashing mit Argon2 und bcrypt, Das Wichtigste auf einen Blick
Immer die Standard-API
password_hash() und password_verify() statt md5, sha1 oder eigener Salt-Logik. Salt und Parameter werden automatisch im Hash-String kodiert.
Argon2id als Standardwahl
Memory-hard und resistent gegen GPU-Angriffe. bcrypt bleibt gueltig, benoetigt aber Vorsicht bei Passphrasen ueber 72 Byte.
Pepper als zweite Schicht
Geheimer, anwendungsweiter Wert per hash_hmac() vor dem Hashing kombiniert, ausserhalb der Datenbank gespeichert.
Transparentes Rehashing
password_needs_rehash() nach jedem erfolgreichen Login pruefen und Hashes ohne Zwangs-Reset auf aktuelle Parameter migrieren.
11. FAQ: Passwort-Hashing mit Argon2 und bcrypt
1Was ist der Unterschied zwischen Passwort-Hashing und Verschluesselung?
2PASSWORD_DEFAULT oder explizit PASSWORD_ARGON2ID?
3Was passiert bei bcrypt ueber 72 Byte?
4Argon2i vs. Argon2id?
5Wie waehle ich memory_cost und time_cost?
6Muss ich mich selbst um Salting kuemmern?
7Was bringt ein Pepper zusaetzlich?
8Wie migriere ich alte md5- oder sha1-Hashes?
9Was macht password_needs_rehash() genau?
10Ist bcrypt heute noch sicher genug?
Mironsoft
PHP-Sicherheit, Authentifizierung und Credential-Haerte
Ist euer Passwort-Hashing wirklich auf dem aktuellen Stand?
Wir pruefen bestehende Authentifizierungs-Flows, migrieren veraltete md5- oder sha1-Hashes transparent auf Argon2id und implementieren Pepper-Strategien, Cost-Tuning und Rehashing-Logik nach aktuellem Stand der Technik.
Auth-Flow-Review
Vollstaendige Pruefung von Registrierung, Login und Session-Handling auf Sicherheitsluecken
Legacy-Hash-Migration
Transparente Umstellung von md5, sha1 oder schwachem bcrypt auf Argon2id ohne Zwangs-Reset
Credential-Security-Audit
Pepper-Strategie, Cost-Parameter und Rehashing-Prozesse fuer euren Stack einrichten