Navigation
 Startseite
 Fachbücher
 Forum
 Webmaster News
 Script Newsletter
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Script Mods
 phpBB Adsense Mode

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 TLD Liste
 Webkatalog‑Verzeichnis

Partner
 Sprüche Treff

Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains und...
https://www.Hosterplus.de
Artfiles.de
Bietet Serviceorientierte Internetdienstleistungen...
https://www.Artfiles.de
 
 
 

PHP crypt() : Veraltet & unsicher – warum Sie ab heute nur noch password_hash() einsetzen sollten

Sie befinden sich: Home > Php Tutorial > Verschluesselung von Zeiche...

Verschluesselung von Zeichenketten nach DES


Eintrag am:  12.08.2010
Hits / Besucher:  4301
Sprache:  Deutsch
Kategorie:  Einsteiger Tutorials...
Tutorial Art:  eigenes
Eingetragen von   schubertmedia schubertmedia
 
Beschreibung

Vor gut dreißig Jahren war es ein Fortschritt, überhaupt irgendeine Art von kryptografischer Funktion in eine Programmiersprache einzubauen, die auf Bits und Ciphers basiert. Die UNIX-Funktion crypt() gehörte deshalb zu den frühen Stars in PHP 3 und 4, weil sie mit einer einzigen Zeile Code etwas tat, was bis dahin nur mit externen C-Bibliotheken möglich war: eine Zeichenkette in einen scheinbar unlesbaren Zeichensalat verwandeln.

Illustration eines gelben Vorhängeschlosses mit PHP-Logo vor blau-violettem Code-Hintergrund; ein Pfeil von „crypt()“ zeigt zum Schloss, ein zweiter Pfeil führt weiter zu „password_hash()“ und symbolisiert den Wechsel zu sicherem Passwort-Hashing.

Das klassische Beispiel – und das haben unzählige Lehrbücher bis heute unverändert abgedruckt – ist das Speichern eines Passworts:

$hash = crypt('passwort');

Doch die Zeiten haben sich dramatisch verändert. Rechenleistung ist milliardenfach gewachsen, dedizierte Passwort-Cracker (z. B. Hashcat) durchsuchen in Sekundenbruchteilen komplette Wörterbücher, und selbst Mittelklasse-Grafikkarten erreichen heute Billionen Hashes pro Sekunde. Was früher als „ausreichend sicher“ galt, ist für aktuelle Angreifer kaum mehr als ein feuchter Pappdeckel.

Genau hier liegt das Problem von crypt() – und die dringende Motivation für diesen Artikel. Wir werden

  1. kurz erklären, was crypt() eigentlich macht und warum es historisch so populär war,

  2. konkret zeigen, warum seine Verwendung inzwischen gefährlich ist,

  3. die sichere, eingebaute Modern-Alternative password_hash() samt Ökosystem (password_verify(), password_needs_rehash(), password_algos()) vorstellen,

  4. einen praxiserprobten Migrationspfad skizzieren, der alte crypt()-Hashes nahtlos in sichere Hashes überführt, und

  5. häufige Missverständnisse aufräumen – vor allem die Verwechslung von Hashing und Verschlüsselung.

Dabei integrieren wir den klassischen Lehrbuchcode (siehe Ursprungs-Text) und zerlegen ihn Schritt für Schritt, um zu verdeutlichen, wo genau die Fallstricke liegen. Am Ende sollen Sie nicht nur wissen, warum crypt() nicht mehr zeitgemäß ist, sondern vor allem wie Sie Ihre Anwendung innerhalb weniger Stunden auf ein zukunftssicheres Fundament stellen.

1 Was ist crypt() und wofür wurde es früher verwendet?

crypt(string $password, string $salt = null): string ist eine Einweg-Hashfunktion. „Einweg“ bedeutet: Aus dem Rückgabewert (dem Hash) lässt sich das Originalpasswort mit vertretbarem Aufwand nicht zurückrechnen. Der klassische Zweck war also, Passwörter „gesichert“ abzulegen, ohne sie im Klartext zu speichern.

Der Ursprungs-Text demonstriert das sehr anschaulich:

$zeichenkette = 'passwort';


echo 'Ohne SALT: '.crypt($zeichenkette).PHP_EOL;
echo 'Mit SALT: '.crypt($zeichenkette, 'test').PHP_EOL;

  • Ohne Salt erzeugt crypt() automatisch ein zufälliges Salt. Jeder Funktionsaufruf liefert daher einen anderen Hash.

  • Mit Salt kommt reproduzierbare Konsistenz ins Spiel: solange dieselbe Passwort-Salt-Kombination eingegeben wird, ist der Hash identisch.

Die Mechanik dahinter adaptierte über die Jahre unterschiedliche UNIX-Algorithmen (DES, Extended DES, MD5, Blowfish, SHA-256, SHA-512 …). Welcher Algorithmus tatsächlich zur Verfügung steht, hängt dabei vom Betriebssystem und von der zur Laufzeit geladenen C-Bibliothek ab – ein erster Hinweis auf die Inkonsistenz, die wir später noch genauer beleuchten.

Historischer Nutzen

Ende der 1990er-Jahre war das alles trotzdem ein Segen. Legacy-Passwortdateien /etc/shadow setzten bereits auf DES- oder MD5-basierte crypt()-Hashes, und PHP-Entwickler konnten mit demselben Aufruf kompatible Hashes erzeugen. In Zeiten von 300-MHz-Pentium-II-CPUs war DES trotz seiner Schwächen realistisch kaum in Sekunden bruteforce-bar. Praktikabel, bewährt, schnell – gut genug.

Illustration zeigt eine Wippe mit DES-Hashes der 1990er-Jahre auf der einen Seite (als sicher angesehen, kaum bruteforce-bar) und DES-Hashes heute auf der anderen Seite (als gefährlich und leicht angreifbar beschrieben). Die Wippe kippt in Richtung der heutigen Unsicherheit.

Dass aus „gut genug“ inzwischen brandgefährlich geworden ist, liegt an vier Entwicklungen:

  1. Mooresches Gesetz & Grafikprozessoren: rohe Hash-Power stieg millionenfach.

  2. Öffentliche Rainbow-Tables für DES/MD5 wandelten offline-Angriffe in trivialen Nachschlage-Aufwand.

  3. Sicherheitserkenntnisse: Algorithmen wie DES haben strukturelle Schwächen (z. B. effektive Länge von nur 56 Bit Schlüssel).

  4. Komfortfunktionen in PHP ab Version 5.5 (2013): password_hash() & Co. machten sicheres Hashing kinderleicht.

2 Die Sicherheitsprobleme von crypt() im Detail

Trotz seiner langen Geschichte weist crypt() zahlreiche Schwächen auf, die in modernen Anwendungen erhebliche Sicherheitsrisiken darstellen. Die folgenden Abschnitte beleuchten diese im Detail.

2.1 Veraltete Algorithmen

Viele crypt()-Modi sind kryptografisch nicht länger ausreichend:

Modus Maximale Schlüssellänge Stand 2025
DES 56 Bit seit 1999 gebrochen (Distributed .net DES)
MD5 128 Bit Hash, aber schnelles Design 2004 erste Kollisionsangriffe; 2012 praktisch
SHA-256/512 256/512 Bit zwar kryptografisch robust, aber viel zu schnell für Passwort-Hashing
Blowfish (BCrypt) variabel heute noch solide, aber crypt() bietet nur begrenzt Cost-Kontrolle

Ein Kennwort, das 1998 in einem 13-Zeichen-MD5-Hash gespeichert wurde, ist 2025 oft in weniger als einer Sekunde vorhanden – entweder als Fund in öffentlichen „Have I Been Pwned“-Dumps oder nach kurzem GPU-Bruteforce.

2.2 Plattformabhängiges Verhalten

Unter Linux mit glibc 2.38 stehen sieben Hash-Formate zur Verfügung, während Windows-PHP-Builds häufig nur Blowfish und MD5 kennen. Ein Hash, der auf System A erzeugt wurde, lässt sich auf System B eventuell nicht verifizieren.

Für Entwicklerteams mit Docker-Containern, CI/CD-Pipelines und mehreren Zielplattformen ist das ein Albtraum.

2.3 Fehleranfällige Salt-Handhabung

crypt() erwartet einen von außen mitgebrachten Salt (außer Sie lassen PHP automatisch einen generieren). Die Regeln dafür unterscheiden sich je nach Algorithmus – DES braucht exakt zwei ASCII-Zeichen, SHA-512 hingegen 66rounds=5000$ + ≥ 8 Zeichen Salt. Solche Details vergessen Menschen leicht. Ein zu kurzes Salt oder ein Copy-&-Paste-Fehler kann die gesamte Passwortdatenbank entwerten, weil identische Passwörter nun identische Hashes produzieren.

2.4 Keine zeitgemäße Cost-Funktion

Moderne Passwort-Hashalgorithmen verlangsamen jeden Hashvorgang absichtlich (Stichwort Key-Stretching). Dadurch wird Brute-Force rechnerisch teuer. crypt() bietet dies nur für Blowfish (via 2y$-Präfix) und auch dort ist die Konfiguration umständlich: Sie müssen den Logarithmus der Runden in den Salt-String einbetten. Fehler sind vorprogrammiert.

2.5 Nur die ersten acht Zeichen zählen – bei DES

Im DES-Modus ignoriert crypt() alles nach dem achten Zeichen. Das ist nicht offensichtlich und führt dazu, dass „LangPasswort!“ und „LangPasswort!MitZusatz“ denselben Hash liefern. Angreifer lieben solche verkappten Schwachstellen.

3 password_hash() – das sichere Standard-Werkzeug ab PHP 5.5

2013 führte PHP 5.5 eine neue High-Level-API ein:

string password_hash(

string $password,
string|int|null $algo = PASSWORD_DEFAULT,
array $options = []
): string

3.1 Warum password_hash() besser ist

Die folgende Tabelle zeigt zentrale Unterschiede zwischen crypt() und password_hash() – insbesondere im Hinblick auf Sicherheit, Wartbarkeit und Handhabung.

Feature crypt() password_hash()
Sichere Algorithmen abhängig vom Betriebssystem BCrypt, Argon2i, Argon2id (alle bewährt & einstellbar)
Salt-Generierung manuell, fehleranfällig automatisch, kryptografisch sicher
Kostenparameter nur Blowfish umständlich einheitlich via cost bzw. memory_cost, time_cost
Zukunftssicherheit kein Auto-Upgrade PASSWORD_DEFAULT wandert mit PHP-Version mit
API-Einfachheit 2 Parameter, viel Vorwissen nötig 1 Funktion + 2 Hilfsfunktionen reichen

Kurz: Sie müssen nichts über Salt-Längen, Präfixe oder DES-Fallstricke wissen. Sie schreiben:

$hash = password_hash($plainPassword, PASSWORD_DEFAULT);

und PHP erledigt den Rest absolut korrekt.

3.2 password_verify() – Passwörter prüfen

if (password_verify($loginEingabe, $hashAusDatenbank)) {

// Erfolg
}

Die Funktion erkennt automatisch, welcher Algorithmus im Hash kodiert ist (er steckt als Präfix darin) und nutzt den passenden Verifizierungsweg. Gleichzeitig schützt sie vor Timing-Angriffen – etwas, das viele Eigenbauten mit hash_equals() oder === falsch machen.

3.3 password_needs_rehash() – Hashes aktuell halten

Gestern haben Sie Ihre Anwendung auf Argon2id mit memory_cost = 6 560 kB aktualisiert, heute möchten Sie die Kosten verdoppeln? Kein Problem:

$options = ['memory_cost' => 131072, 'time_cost' => 4];

if (password_needs_rehash($hashAusDb, PASSWORD_ARGON2ID, $options)) {
$hashNeu = password_hash($plainPassword, PASSWORD_ARGON2ID, $options);
// in DB speichern
}

Beim nächsten Login aktualisieren Sie Hashes ganz nebenbei, ohne dass Benutzer etwas merken.

3.4 password_algos() – verfügbare Algorithmen abfragen

Ein kurzer Blick genügt:

print_r(password_algos());

// → Array ( [0] => 2y [1] => argon2i [2] => argon2id )

Damit können Installer-Skripte oder Diagnose-Tools prüfen, ob alle Zielserver dieselben Möglichkeiten bieten.

4 Migration von alten crypt()-Hashes

Bevor bestehende crypt()-Hashes durch moderne Alternativen ersetzt werden können, braucht es eine Migrationsstrategie, die Benutzer nicht stört und bestehende Logins nicht unterbricht.

4.1 Prinzip der Lazy Migration

  1. Login des Benutzers: Passwort-Eingabe wird entgegengenommen.

  2. password_verify() prüft das Passwort gegen den gespeicherten Hash. Ja, wirklich! Die Funktion erkennt auch klassische crypt()-Formate (alle außer sehr alten DES-Hashes ohne Präfix).

  3. Wenn korrekt → sofort neues, sicheres Hash mit password_hash() bilden.

  4. Hash in der Datenbank ersetzen, Session starten, fertig.

So wird jeder Benutzer automatisch migriert, sobald er sein Konto nutzt. Keine Massen-Passwort-Resets, keine Wartungsfenster.

4.2 Code-Beispiel

$userInput = $_POST['password'];

$oldHash = $row['password']; // kommt aus SELECT …

if (password_verify($userInput, $oldHash)) {
if (password_needs_rehash($oldHash, PASSWORD_DEFAULT)) {
$new = password_hash($userInput, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('UPDATE users SET password = ? WHERE id = ?');
$stmt->execute([$new, $row['id']]);
}
// normale Login-Routine
} else {
// Fehler
}

4.3 Was tun mit unleserlichen DES-Hashes?

Manche Uralt-Systeme nutzen zweistellige Salts und keine Präfixe. password_verify() erkennt sie nicht. Hier bleiben nur zwei Wege:

  • Forcierter Passwort-Reset: Benutzer klickt auf „Passwort vergessen“, bekommt Mail, setzt neues Passwort – jetzt mit modernen Hashes.

  • Offline-Rehash nach Klartext-Extraktion – nicht zu empfehlen, weil dazu sämtliche Passwörter im Klartext vorliegen müssen (riesiger Compliance-Bruch).

5 Hashing ≠ Verschlüsseln – häufige Verwechslungen

  • Hashing ist Einweg (irreversibel). Geeignet für Passwörter.

  • Verschlüsselung ist Zweiweg (reversibel). Geeignet für vertrauliche Daten wie Kreditkartennummern, die man lesen können muss.

PHP bietet dafür:

Aufgabe Moderne API
Symmetrische Verschlüsselung sodium_crypto_secretbox_* (Libsodium)
Asymmetrische Verschlüsselung sodium_crypto_box_* / OpenSSL (openssl_*)
Passwort-Hashing password_hash(), password_verify()

mcrypt ist seit PHP 7.2 entfernt, sein Einsatz gilt als höchst unsicher.

6 Best-Practice-Checkliste für sicheres Passwort-Handling in PHP (2025-Edition)

  1. PHP ≥ 8.1 einsetzen – Argon2id ist standardmäßig verfügbar.

  2. password_hash($pwd, PASSWORD_DEFAULT) (Argon2id) nutzen, als Fallback PASSWORD_BCRYPT.

  3. Datenbank-Feldbreite mindestens VARCHAR(255) – ein Argon2id-Hash ist ~97 Zeichen lang.

  4. Immer password_verify() statt manueller Vergleiche nutzen.

  5. In Login-Routine password_needs_rehash() einbauen, um Kostenparameter aktuell zu halten.

  6. Nach Möglichkeit Rate-Limiting und/oder captcha nach X Fehlversuchen implementieren.

  7. Pepper (zusätzlicher geheimer Schlüssel auf Server) kann Brute-Force-Aufwand weiter erhöhen, sollte aber sorgsam verwaltet werden (z. B. in ENV- Variablen außerhalb des Repos).

  8. 2-Faktor-Authentifizierung ergänzt Passwort-Sicherheit, ersetzt sie aber nicht.

  9. Offene Bibliotheken wie Symfony Security oder Laravel Sanctum verwenden, anstatt eigene Auth-Frameworks zu basteln.

  10. Regelmäßige Audits: Hash-Kosten jedes Jahr prüfen; Moderne GPUs werden schneller.

7 Zusammenfassung & Handlungsaufforderung

  • crypt() war lange ein akzeptabler Kompromiss, ist 2025 aber faktisch unsicher.

  • Sein Hauptproblem ist nicht ein einzelner Bug, sondern eine Kombination aus zu schnellen Algorithmenplattformspezifischem Verhalten und komplizierter Salt-Verwaltung.

  • PHP liefert seit über zehn Jahren eine einfachere, stärkere und zukunftssichere Lösung: password_hash() samt Ökosystem.

  • Die Umstellung erfordert wenige Code-Zeilen und kann per Lazy Migration ohne Benutzereingriff erfolgen.

  • Jeder Tag, den ein Produktivsystem noch crypt() nutzt, ist ein Tag, an dem Angreifer weniger Rechenzeit benötigen.

Handeln Sie jetzt: Suchen Sie in Ihrem Projekt nach crypt(, ersetzen Sie jeden Fund durch password_hash() / password_verify(), und implementieren Sie die oben gezeigte Migrationsroutine. So schützen Sie nicht nur Passwörter, sondern auch das Vertrauen Ihrer Nutzer – und schlafen selbst besser.

Vollständiges Beispielskript

<?php

declare(strict_types=1);
require 'db.php'; // PDO $pdo

function register(string $user, string $pwd): void
{
global $pdo;
$hash = password_hash($pwd, PASSWORD_DEFAULT);
$pdo->prepare('INSERT INTO users (name, password) VALUES (?,?)')
->execute([$user, $hash]);
}

function login(string $user, string $pwd): bool
{
global $pdo;
$stmt = $pdo->prepare('SELECT id, password FROM users WHERE name = ? LIMIT 1');
$stmt->execute([$user]);
$row = $stmt->fetch();

if (!$row || !password_verify($pwd, $row['password'])) {
return false;
}

// Hash ggf. aktualisieren
if (password_needs_rehash($row['password'], PASSWORD_DEFAULT)) {
$new = password_hash($pwd, PASSWORD_DEFAULT);
$pdo->prepare('UPDATE users SET password = ? WHERE id = ?')
->execute([$new, $row['id']]);
}

$_SESSION['uid'] = $row['id'];
return true;
}

Dieses kompakte Snippet zeigt alle Kernkonzepte in Aktion – registrieren, einloggen, rehashen. Dank password_*-Funktionen ist es sicher, portabel und wartungsarm.

Schlusswort

Sicherheitslücken entstehen selten durch spektakuläre Zero-Day-Exploits. Meist wurzeln sie in altem Code, den „schon immer alles funktioniert hat“. crypt() ist das Paradebeispiel: Er erfüllt bis heute seinen Vertrag – ein String geht rein, ein Hash kommt raus. Aber der Kontext hat sich geändert.

Jetzt ist es an Ihnen, die Lücke zwischen Historie und Gegenwart zu schließen. Mit password_hash() ist das so einfach wie nie zuvor. Machen Sie den Schritt – heute.

 

Tags:

 

Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.