PHP-Hash-Funktionen im Vergleich: Länge, Tempo und Einsatzzweck

Eine Hash-Funktion bildet eine beliebig lange Eingabe auf einen Wert fester Länge ab. PHP 8.4.6 bringt dafür in hash_algos() 60 Verfahren mit (die Zahl hängt von Version und Build ab), dazu kommen BLAKE2b und SipHash aus der Erweiterung sodium. Meist braucht man sie für Cache-Schlüssel, Prüfsummen und kurze Codes für Short Links, selten für Passwörter; dann zählen Tempo und Kürze. Welches Verfahren du nimmst, hängt vom Zweck ab. Ich habe alle 62 Verfahren gemessen (Stand 29.10.2026, PHP 8.4.6) und die Ergebnisse so aufbereitet, dass du dir dein Verfahren für deinen Zweck heraussuchen kannst.

Vier Aufgaben, vier Hash-Verfahren in PHP In meiner Messung gilt: Für Prüfsummen und Cache-Schlüssel passen xxh3 und crc32c mit etwa 50 Gigabyte pro Sekunde. Für Integrität und Tokens passt SHA-256 mit etwa 4 Gigabyte pro Sekunde. Für Hash-Tabellen mit fremden Eingaben passt SipHash aus sodium mit etwa 3,8 Gigabyte pro Sekunde. Für Passwörter gehört password_hash mit Argon2id, ein Hash dauert dort etwa 0,16 Sekunden. Prüfsumme und Cache-Schlüssel xxh3 · crc32c rund 50 GB/s Integrität und Tokens SHA-256 · HMAC rund 4 GB/s Hash-Tabelle bei fremden Eingaben SipHash (sodium) rund 3,8 GB/s Passwörter password_hash() Argon2id 0,16 s je Hash
Abbildung 1: Vier Aufgaben, vier Verfahren. Tempo gemessen bei 1 MiB Eingabe, bei Passwörtern die Zeit für einen Hash.

Welcher Hash passt zu welchem Zweck?

Welches Verfahren passt, hängt vom Zweck ab. Für Prüfsummen, Cache-Schlüssel und Deduplizierung genügt ein nicht kryptografischer Hash wie xxh3 oder crc32c. Für Integrität, Signaturen und Tokens nimmst du SHA-256, mit hash_hmac() bei Schlüsselbezug. Passwörter gehören ausschließlich in password_hash(). MD5 und SHA-1 taugen nur noch für Altformate.

Zweck Verfahren in PHP Warum
Prüfsumme gegen Übertragungsfehler, ETag crc32c, xxh3 sehr schnell, zufällige Fehler erkennt schon ein 32-Bit-Wert
Cache-Schlüssel, Deduplizierung, wenn niemand die Eingabe gezielt wählt xxh3 (64 Bit), xxh128 (128 Bit) schnell bei großen Eingaben, 64 Bit kollidieren erst bei Milliarden Werten; bei Eingaben von außen sha256 oder hash_hmac() mit Schlüssel
Kurzer Code für Short Links Zähler in base62, Zufall (random_bytes()) oder gekürzter sha256 der Zähler ist am kürzesten und kollisionsfrei; gekürzte Hashes brauchen mindestens 8 Zeichen und einen UNIQUE-Index
Hash-Tabelle mit fremden Eingaben sodium_crypto_shorthash() (SipHash) mit geheimem Schlüssel aus random_bytes() schlüsselabhängig, ein Angreifer ohne Schlüssel kann Kollisionen nicht vorhersagen
Integrität gegen Zufallsfehler, Token-Ablage sha256 kryptografisch, in jedem PHP-Build vorhanden
Echtheit einer Nachricht (Signatur) hash_hmac('sha256', …) mit geheimem Schlüssel oder eine echte Signatur ein nacktes SHA-256 beweist keine Herkunft
Kryptografischer Hash mit Schlüsselmodus sodium_crypto_generichash() (BLAKE2b) kryptografisch, braucht ext-sodium; kein Passwort-Hash. Schlüssel aus Schlüsselmaterial leitest du mit hash_hkdf() ab, Passphrasen gehören in password_hash()
Passwörter password_hash() mit PASSWORD_ARGON2ID absichtlich langsam und speicherhungrig
Altformate, Kompatibilität md5, sha1 nur wenn das Fremdsystem es verlangt

So sieht das im Code aus:

// Cache-Schlüssel, ETag, Deduplizierung: nicht kryptografisch genügt, solange niemand die Eingabe gezielt wählt
$key  = hash('xxh3', $routeName);            // 16 Hex-Zeichen; $routeName stammt aus dem eigenen Code (bei Eingaben von außen: sha256)
$etag = '"' . hash('crc32c', $body) . '"';   // 8 Hex-Zeichen

// Integrität und Tokens: kryptografisch
$tokenHash = hash('sha256', $token);         // $token = random_bytes(32), nicht erratbar
$mac       = hash_hmac('sha256', $body, $secret);
if (!hash_equals($expectedMac, $mac)) {
  // ablehnen
}

// Passwörter: nie hash(), immer password_hash()
$stored = password_hash($password, PASSWORD_ARGON2ID);
if (password_verify($input, $stored) and password_needs_rehash($stored, PASSWORD_ARGON2ID)) {
  $stored = password_hash($input, PASSWORD_ARGON2ID);  // Kosten über die Jahre anheben
}

Wer Cache-Schlüssel in APCu ablegt, findet die passende Umgebung in APCu Shared Memory für PHP konfigurieren.


Wie schnell sind die PHP-Hash-Verfahren?

Bei 1 MiB Eingabe schaffen crc32b, crc32c und xxh3 auf meinem Rechner 49 bis 53 GB/s, SHA-256 4,2 GB/s, SHA-1 und MD5 unter 1 GB/s, SHA-512 0,64 GB/s. SHA-256 liegt damit rund fünfmal vor MD5. Die langsamsten Altverfahren (md2, snefru, gost) erreichen nur 19 bis 85 MB/s.

Durchsatz ausgewählter Hash-Verfahren bei 1 MiB Eingabe in PHP 8.4 Gemessen in Gigabyte pro Sekunde: crc32c 52,7, xxh3 49,0, xxh64 23,7, SHA-256 4,2, SipHash 3,8, BLAKE2b 1,85, SHA-1 0,95, MD5 0,80, SHA3-256 0,78, SHA-512 0,64. Die nicht kryptografischen Verfahren crc32c, xxh3, xxh64 und SipHash sind hell dargestellt, die kryptografischen und gebrochenen Verfahren farbig. Verfahren Durchsatz bei 1 MiB, länger ist schneller crc32c 52,7 GB/s xxh3 49,0 GB/s xxh64 23,7 GB/s SHA-256 4,2 GB/s SipHash 3,8 GB/s BLAKE2b 1,85 GB/s SHA-1 0,95 GB/s MD5 0,80 GB/s SHA3-256 0,78 GB/s SHA-512 0,64 GB/s
Abbildung 2: Durchsatz bei 1 MiB Eingabe (PHP 8.4.6, Windows x64). Helle Balken: nicht kryptografische Verfahren; farbige Balken: kryptografische und gebrochene (SHA-1, MD5).

Die Tabelle zeigt dreizehn Verfahren in drei Eingabegrößen. Die Spalte „16 B“ ist die Zeit für einen einzelnen Aufruf von hash() mit 16 Byte Eingabe, die beiden anderen sind Durchsatz. Die vollständige Liste aller 62 Verfahren gibt dir das Messskript weiter unten für deinen eigenen Server aus.

Verfahren Bits Hex-Zeichen 16 B 4 KiB 1 MiB
crc32b (= crc32()) 32 8 44 ns 33,9 GB/s 52,3 GB/s
crc32c 32 8 45 ns 33,7 GB/s 52,7 GB/s
xxh3 64 16 40 ns 31,7 GB/s 49,0 GB/s
xxh128 128 32 48 ns 29,8 GB/s 49,0 GB/s
xxh64 64 16 40 ns 19,5 GB/s 23,7 GB/s
murmur3f 128 32 38 ns 9,2 GB/s 9,9 GB/s
sha256 256 64 70 ns 3,8 GB/s 4,2 GB/s
siphash-2-4 (sodium) 64 16 33 ns 3,7 GB/s 3,8 GB/s
blake2b (sodium) 256 64 116 ns 1,8 GB/s 1,85 GB/s
sha1 160 40 110 ns 922 MB/s 950 MB/s
md5 128 32 115 ns 771 MB/s 800 MB/s
sha3-256 256 64 229 ns 730 MB/s 779 MB/s
sha512 512 128 256 ns 618 MB/s 639 MB/s

Fünf Beobachtungen aus der Messung:

  • SHA-256 ist auf diesem Rechner rund fünfmal schneller als MD5. Bei 4 KiB ist es das 5,0-Fache, bei 1 MiB das 5,2-Fache. Die verbreitete Annahme „MD5 ist schnell, SHA-256 ist sicher, aber langsam“ stimmt hier nicht. Wie weit die CPU das Tempo beeinflusst, hängt vom Prozessor und vom PHP-Build ab. Miss deshalb auf deinem Server.
  • xxh3 ist so schnell wie crc32 und hat 64 statt 32 Bit. Für Cache-Schlüssel ist das der bessere Kompromiss, denn 32 Bit kollidieren zu früh (nächster Abschnitt).
  • Bei 16 Byte bestimmt der Aufrufrahmen mit. Schon eine leere Schleife kostet 7 ns, der Aufruf von hash() mit der schnellsten Prüfsumme 44 ns. xxh3 braucht 40 ns, SHA-256 70 ns, MD5 115 ns. Bei kurzen Schlüsseln liegen die Unterschiede damit im Bereich von Nanosekunden und fallen gegen einen Datenbank- oder Dateizugriff nicht ins Gewicht.
  • Die Kurzformen md5(), sha1() und crc32() sparen 9 bis 17 ns je Aufruf, Hex- statt Binärausgabe kostet bei SHA-256 rund 13 ns. Beides zählt erst bei Millionen Aufrufen.
  • Die Altverfahren sind sehr langsam. whirlpool schafft 201 MB/s, gost 84 MB/s, snefru 47 MB/s, md2 19 MB/s, das 260- bis 2.800-Fache langsamer als crc32c. Diese Verfahren gibt es aus Kompatibilitätsgründen, für neue Anwendungen brauchst du sie nicht.

Wo ein Hash in einer Anwendung wirklich Zeit kostet, zeigt erst das Profil. Bei einem Cache-Schlüssel mit MD5 sind es 115 ns, die Abfrage oder Dateioperation dahinter braucht Millisekunden. Die häufigeren Bremsen beschreibt PHP-Performance: die acht teuersten Fehler im Code.


Welcher Hash eignet sich für Cache-Schlüssel und Short Links?

Für Cache-Schlüssel ist xxh3 bei URL-langen Eingaben am schnellsten (54 ns gegen 212 ns für MD5), solange niemand die Eingabe gezielt wählt. Für Short Links ist ein Zähler in base62 am kürzesten und kollisionsfrei: 6 Zeichen reichen für 56 Milliarden Links. Ein gekürzter Hash braucht mindestens 8 Zeichen und eine Kollisionsbehandlung.

Cache-Schlüssel: schnell und kurz

Ein Cache-Schlüssel entsteht bei jedem Zugriff neu, deshalb zählt der Hash pro Aufruf. Gemessen mit einer URL von 90 Byte als Eingabe und Hex-Ausgabe, wie im Betrieb:

Verfahren Länge als Hex Zeit je Schlüssel
xxh3 16 Zeichen 54 ns
crc32b 8 Zeichen 67 ns
xxh128 32 Zeichen 67 ns
sha256 64 Zeichen 111 ns
sha1 40 Zeichen 196 ns
md5 32 Zeichen 212 ns
  • xxh3 ist bei dieser Eingabe viermal schneller als MD5 und halb so lang. crc32b ist kürzer, aber mit 32 Bit als alleiniger Schlüssel zu kollisionsanfällig (siehe unten).
  • Der Hash fällt gegen den Zugriff dahinter kaum ins Gewicht. Auf Cache, Datei oder Datenbank wartest du länger als auf die Berechnung; entscheidend sind Schlüssellänge und Kollisionsfreiheit.
  • Rohbytes halbieren die Länge. hash('xxh3', $key, true) liefert 8 statt 16 Zeichen. Ob dein Cache-Backend Binärschlüssel erlaubt, steht in dessen Dokumentation; sonst nimm Hex oder base64url.
  • Ein Präfix mit Version macht das Leeren einfach. Mit 'artikel:v3:' . hash(...) entwertest du alle Schlüssel einer Gruppe, indem du die Version erhöhst.
  • Eingaben von außen brauchen ein kryptografisches Verfahren. sha256 kostet 111 ns und lässt sich auf 32 Hex-Zeichen (128 Bit) kürzen.

Short Links: Zähler, Zufall oder gekürzter Hash?

Für kurze Codes gibt es drei Wege. Die Rechenzeit liegt bei 50 bis 170 ns und spielt gegen den Datenbankzugriff keine Rolle. Entscheidend sind Länge, Kollisionen und die Frage, ob Codes erratbar sein dürfen.

Weg Beispiel in PHP Zeit Kollisionen Erratbar
Zähler in base62 toBase62($id) 168 ns keine ja, aufeinanderfolgende Codes
Zufall bin2hex(random_bytes(4)) 53 ns möglich, UNIQUE-Index nötig nein
Hash der URL, gekürzt base64url(substr(hash('xxh3', $url, true), 0, 6)) 105 ns möglich, UNIQUE-Index nötig nein; gleiche URL ergibt gleichen Code

Wie lang ein Code sein muss, hängt von der Zahl der Links ab. Die Tabelle zeigt für zufällig verteilte Codes, ab wann Kollisionen wahrscheinlich werden:

Länge des Codes Mögliche Codes Kollision zu 50 % bei Bei 100.000 Links Bei 1 Million Links
8 Hex-Zeichen (32 Bit) 4,3 Milliarden 77.000 69 % praktisch sicher
6 Zeichen base62 56,8 Milliarden 281.000 8,4 % praktisch sicher
7 Zeichen base62 3,5 Billionen 2,2 Millionen 0,14 % 13 %
8 Zeichen base62 218 Billionen 17,4 Millionen 0,002 % 0,23 %
8 Zeichen base64url (48 Bit) 281 Billionen 19,8 Millionen 0,002 % 0,18 %

Aus der Tabelle folgen vier Regeln:

  • Acht Hex-Zeichen taugen nicht als Short-Link-Code. Bei 100.000 Links kollidieren sie mit 69 % Wahrscheinlichkeit.
  • Gekürzte Hashes brauchen mindestens 8 Zeichen aus base62 oder base64url und immer einen UNIQUE-Index auf dem Code. Bei einer Kollision hashst du mit einem Zähler als Salz erneut.
  • Kommen die URLs von Nutzern, nimm sha256 statt xxh3. Mit einem nicht kryptografischen Hash kann ein Angreifer Kollisionen gezielt erzeugen. SHA-256 kostet rund 60 ns mehr.
  • Aufeinanderfolgende Codes lassen sich aufzählen. Sollen Links nicht öffentlich sein, nimm Zufall statt eines Zählers.

Zwei Bausteine dafür:

// Zähler → base62: die kürzesten Codes, keine Kollisionen
function toBase62(int $number): string
{
  $alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  $out = '';
  do {
    $out = $alphabet[$number % 62] . $out;
    $number = intdiv($number, 62);
  } while ($number > 0);
  return $out;
}
echo toBase62(12_345_678);                   // PNFQ (4 Zeichen)
echo toBase62(56_800_235_583);               // ZZZZZZ: 6 Zeichen reichen für 56,8 Milliarden Links

// Hash der URL → 8 Zeichen base64url (48 Bit), gleiche URL ergibt gleichen Code
$code = rtrim(strtr(base64_encode(substr(hash('sha256', $url, true), 0, 6)), '+/', '-_'), '=');

Wie lang ist ein Hash, und wann kollidiert er?

Vier Bit ergeben ein Hex-Zeichen: crc32 liefert 8 Zeichen, xxh3 16, MD5 32, SHA-1 40, SHA-256 64 und SHA-512 128. Je kürzer der Wert, desto früher kollidieren zufällig verschiedene Eingaben: Ein 32-Bit-Hash erreicht schon bei etwa 77.000 Werten eine Kollisionschance von 50 %, ein 64-Bit-Hash erst bei rund 5 Milliarden.

Länge Beispiele Bytes (binär) Hex-Zeichen Kollision zu 50 % bei etwa
32 Bit crc32b, crc32c, xxh32 4 8 77.000 Werten
64 Bit xxh3, xxh64, SipHash 8 16 5 Milliarden Werten
128 Bit md5, xxh128 16 32 2 · 10¹⁹ Werten
256 Bit sha256, sha3-256, blake2b 32 64 praktisch nie

Die Werte folgen dem Geburtstagsparadox: Eine Kollision wird wahrscheinlich, sobald etwa 1,18 · 2^(n/2) verschiedene Werte gehasht wurden, bei n Bit Länge. Das gilt für zufällig verteilte Eingaben. Nicht kryptografische Hashes wie crc32 oder xxh3 schützen nicht vor einem Angreifer, der Eingaben gezielt wählt: Er kann Kollisionen erzeugen. Dagegen hilft nur ein kryptografisches Verfahren, und auch das nicht, wenn es MD5 oder SHA-1 heißt.

Zwei Praxisregeln folgen daraus:

  • Speicher halbieren: hash($algo, $data, true) liefert die Rohbytes. Ein SHA-256 braucht dann 32 statt 64 Byte, in PostgreSQL als bytea, in MySQL als BINARY(32).
  • 32-Bit-Werte nur mit Kollisionsbehandlung: Ein crc32 als alleiniger Schlüssel einer Tabelle mit hunderttausend Zeilen kollidiert mit hoher Wahrscheinlichkeit. Als Prüfsumme neben dem Inhalt ist er dagegen richtig.

Vorsicht bei den CRC-Varianten: hash('crc32b', $s) und crc32($s) liefern denselben Wert, hash('crc32', $s) nicht. Das ist die bzip2-Variante mit anderer Byte-Reihenfolge. crc32c verwendet ein anderes Polynom (Castagnoli) und liefert wieder andere Werte. Für „Hallo Welt“ ergibt sich 50281475 (crc32b), 5a9c5320 (crc32) und 5a8fb19f (crc32c).


Warum taugen MD5 und SHA-1 nicht mehr für Sicherheit?

Für beide Verfahren lassen sich Kollisionen gezielt erzeugen: bei MD5 seit 2004, bei SHA-1 seit der ersten öffentlichen Kollision 2017. Ein Angreifer kann damit zwei verschiedene Dokumente mit demselben Hash herstellen. Als Prüfsumme gegen zufällige Fehler und als Cache-Schlüssel sind beide weiter brauchbar, für Signaturen und Integrität nicht.

Es gibt nur noch einen Grund, MD5 oder SHA-1 zu verwenden: Ein Fremdsystem oder ein Dateiformat verlangt es. Der frühere Tempovorteil ist in der Messung nicht mehr sichtbar: SHA-256 war bei 4 KiB und 1 MiB schneller als MD5 und SHA-1. In meiner Messung (Intel Core Ultra 9, PHP 8.4.6) entfällt damit das Tempo-Argument für neue Anwendungen. Ob das auch auf deinem Server gilt, zeigt erst deine eigene Messung.


Warum gehören Passwörter nicht in hash()?

Ein schneller Hash ist für Passwörter ein Fehler, weil ein Angreifer mit einer gestohlenen Hash-Tabelle sehr viele Kandidaten pro Sekunde durchprobiert. password_hash() ist deshalb absichtlich langsam: Ein Argon2id-Hash mit den PHP-Standardwerten dauert 162 ms, ein SHA-256 über 16 Byte 70 ns, ein Faktor von rund 2,3 Millionen.

Verfahren Zeit je Hash Hashes pro Sekunde
sha256 über ein kurzes Passwort 70 ns rund 14 Millionen
bcrypt, cost 10 38 ms 26
bcrypt, cost 12 (PHP-Standard ab 8.4) 152 ms 6,6
bcrypt, cost 14 616 ms 1,6
Argon2id, 64 MiB, t=4, p=1 (PHP-Standard) 162 ms 6,2
Argon2id, 64 MiB, t=4, p=3 65 ms 15
Argon2id, 19 MiB, t=2, p=1 (OWASP-Untergrenze) 21 ms 47
PBKDF2-HMAC-SHA256, 600.000 Iterationen 74 ms 13,5

Die Spalte „Hashes pro Sekunde“ zeigt, was ein einzelner PHP-Prozess auf dieser CPU schafft, nicht die Geschwindigkeit eines Angreifers: Mit Grafikkarten erreicht er bei schnellen Verfahren wie SHA-256 um Größenordnungen mehr. Bei Argon2id mit mehreren Threads (p=3) ist die Wanduhrzeit kürzer, die CPU-Zeit je Anmeldung nicht, und threads über 1 wirkt laut PHP-Handbuch nur in Builds mit libargon2, nicht mit der sodium-Implementierung.

Was daraus folgt:

  • Nimm PASSWORD_ARGON2ID, wenn dein PHP-Build es unterstützt. Speicherhunger macht Angriffe mit Grafikkarten teuer. bcrypt schneidet Passwörter bei 72 Byte ab, lange Passphrasen werden damit nicht vollständig geprüft.
  • Wähle die Kosten nach deiner Anmelde-Last. Die OWASP-Werte sind eine Untergrenze. Wie weit du darüber liegst, bestimmen die Zeit pro Anmeldung und die Zahl gleichzeitiger Anmeldungen auf dem Server.
  • Rechne mit steigenden Kosten. password_needs_rehash() meldet beim nächsten erfolgreichen Login, dass der gespeicherte Hash nicht mehr zu den aktuellen Parametern passt (siehe Codebeispiel oben).
  • Plane die Spaltenlänge großzügig. Die Konstante PASSWORD_DEFAULT kann sich ändern, das Handbuch rät zu mindestens 255 Byte.

Und Tokens?

Ein Login-Link oder ein „angemeldet bleiben“-Token darf als SHA-256 in der Datenbank stehen, wenn der Wert aus random_bytes() stammt und mindestens 128 Bit lang ist. Der Unterschied zum Passwort ist die Entropie: Ein zufälliger 256-Bit-Wert lässt sich nicht erraten, auch nicht mit einer schnellen Hash-Funktion. Ein vom Menschen gewähltes Passwort schon. Geprüft wird über den Datenbank-Lookup des Hashes oder mit hash_equals(), und ein Token braucht ein Ablaufdatum und Einmalnutzung. In meinen eigenen Systemen liegen deshalb Passwörter als Argon2id-Hash und Token nur als SHA-256 ihres Zufallswerts in der Datenbank. Wer die Grundlagen systematisch aufbauen will, findet password_hash() und password_verify() in der Agenda von PHP Programmierung Grundlagen.


Ist ein gehashter Wert anonym?

Ein Hash anonymisiert Werte aus einem kleinen Raum nicht. Bei IPv4-Adressen lässt sich jeder mögliche Wert durchprobieren: Alle 4,3 Milliarden Adressen kosten mit xxh128 hochgerechnet etwa 4 Minuten auf einem Kern, mit SHA-256 etwa 5 Minuten, mit MD5 etwa 8,5 Minuten.

Verfahren Zeit je Kandidat Gesamter IPv4-Raum (2³² Adressen)
xxh128 54,5 ns 3,9 Minuten
sha256 71,3 ns 5,1 Minuten
md5 118,9 ns 8,5 Minuten

Das ist eine Hochrechnung aus der Messung (Eingabe 30 Byte, ein Kern, Median aus 3 Läufen), kein Angriff. Mit mehreren Kernen oder einer Grafikkarte schrumpfen die Zeiten entsprechend. Ein Salt, das im Quelltext steht, ändert daran nichts, und ein kryptografisches Verfahren hilft kaum: SHA-256 ist hier sogar schneller als MD5. Schutz bieten vor allem ein geheimer Schlüssel, der nicht im Code steht (hash_hmac()), und das Kürzen oder Löschen der Adresse; auch ein geschlüsselter Hash bleibt ein Pseudonym. IPv6-Adressen lassen sich nicht vollständig durchprobieren, nach einem bekannten /64-Präfix aber stark eingrenzen.

Pseudonymisierte Daten bleiben für den personenbezogen, der die Zuordnung herstellen kann (Erwägungsgrund 26 DSGVO). Bei IP-Adressen ist das der Betreiber selbst, der alle Adressen durchprobieren kann: Ein Hash macht sie nicht anonym. Anders liegt es bei einem Hash über einen Zufallswert, den niemand einer Person zuordnen kann. Wie du IP-Adressen rechtssicher behandelst, klärst du mit deinem Datenschutzbeauftragten oder einer Rechtsberatung.


So misst du die Hash-Verfahren auf deinem Server

Miss mit einer Schleife, die hash() direkt aufruft, ohne Closure-Rahmen, und werte die beste von mehreren Runden. Schalte Xdebug aus, prüfe die Last auf dem Rechner und wiederhole die Läufe mit Pausen. Das Ergebnis von deinem Server ist belastbarer als jede Tabelle aus diesem Artikel, auch als meine.

Vier Schritte für eine belastbare Hash-Messung Schritt 1: Xdebug ausschalten und die Last auf dem Rechner prüfen. Schritt 2: Eingabegrößen wählen, wie sie im Betrieb vorkommen. Schritt 3: Die beste von 5 Runden werten. Schritt 4: Mehrere Läufe mit Pausen wiederholen. 1 Xdebug aus, Last prüfen 2 Eingabe wie im Betrieb 3 Beste von 5 Runden 4 Mehrere Läufe mit Pause
Abbildung 3: Die vier Schritte einer belastbaren Messung.

Das Skript misst Durchsatz je Verfahren und gibt eine Tabelle aus:

<?php
declare(strict_types=1);

function nsPerCall(string $algo, string $data): float
{
  hash($algo, $data, true);                          // Warmlauf
  $best = INF;
  for ($round = 0; $round < 5; $round++) {           // beste von 5 Runden
    $n = 0;
    $t = hrtime(true);
    do {
      for ($i = 0; $i < 100; $i++) {
        hash($algo, $data, true);                    // direkter Aufruf, kein Closure-Rahmen
      }
      $n += 100;
      $elapsed = hrtime(true) - $t;
    } while ($elapsed < 25_000_000);                 // jede Runde mindestens 25 ms
    $best = min($best, $elapsed / $n);
  }
  return $best;
}

$data  = random_bytes(4096);
$algos = hash_algos();
shuffle($algos);                                     // feste Reihenfolge verzerrt bei Dauerlast
foreach ($algos as $algo) {
  $ns = nsPerCall($algo, $data);
  printf("%-12s %4d Bit %8.0f MB/s\n", $algo, strlen(hash($algo, '', true)) * 8, strlen($data) / $ns * 1000);
}

Aufruf: php -d xdebug.mode=off hash-benchmark.php. Wer eine fertige Messklasse mit Prozentvergleich („100 % = am schnellsten“) bevorzugt, findet sie in meinem Open-Source-Repository perf auf GitHub.

Meine eigene Messung hat sich dreimal selbst widerlegt, und das ist der Teil, den ich für wichtig halte:

  • Erste Fassung: Aufrufrahmen mitgemessen. Ich rief das Verfahren über eine Closure pro Durchlauf auf. Das kostet rund 40 ns und machte bei 16 Byte alle schnellen Verfahren gleich schnell. Die direkte Schleife zeigte den Unterschied.
  • Zweite Fassung: Laptop gedrosselt. Nach etwa 30 Sekunden Dauerlast fielen die Werte auf die Hälfte (SHA-256 bei 1 MiB: 1,7 statt 4,1 GB/s), die Streuung stieg auf bis zu 37 %. Argon2id maß im Sammellauf 349 ms, in einem eigenen Prozess mit Pause 162 ms. Deshalb besteht die Messung jetzt aus Läufen von 30 Sekunden mit Pausen, und jede Passwort-Variante läuft in einem eigenen Prozess.
  • Dritte Fassung: feste Reihenfolge. Alle 62 Verfahren liefen in jedem Lauf in derselben Reihenfolge, also landeten dieselben am Ende, wenn der Laptop schon drosselte: crc32c kam auf 22 statt 53 GB/s, BLAKE2b auf 0,78 statt 1,85 GB/s. Seit das Skript die Reihenfolge je Lauf mischt, streuen die Läufe nur noch um 1,7 %, und die Werte decken sich mit meinem ersten Lauf auf dem kühlen Rechner.

Die Umgebung wirkt auch im Betrieb: Wie du OPcache, FPM und die Pool-Größe dafür einstellst, beschreibt PHP-Konfiguration: OPcache und FPM richtig einstellen. Das systematische Messen und Gegenmessen von Flaschenhälsen ist Teil von PHP OOP Fortgeschrittene Technologien.


Häufige Fragen (FAQ)

Welcher Hash ist in PHP am schnellsten?

In meiner Messung sind es bei 1 MiB Eingabe crc32c, crc32b und xxh3 mit 49 bis 53 GB/s, gefolgt von xxh128 mit 49 GB/s. Alle vier sind nicht kryptografisch. Der schnellste kryptografische Hash ist SHA-256 mit etwa 4 GB/s. Bei 16 Byte Eingabe bestimmt der Funktionsaufruf mit: 33 bis 48 ns für die schnellen Verfahren, 70 ns für SHA-256, 115 ns für MD5.

Welchen Hash nehme ich für einen Cache-Schlüssel?

xxh3 oder xxh128, solange niemand die Eingabe gezielt wählt: Bei einer URL mit 90 Byte brauchte xxh3 54 ns und liefert 16 Hex-Zeichen, MD5 brauchte 212 ns bei 32 Zeichen. Kommt die Eingabe von außen, nimm sha256 (111 ns), bei Bedarf auf 32 Hex-Zeichen gekürzt.

Wie erzeuge ich einen kurzen Code für einen Short Link in PHP?

Am kürzesten wird ein Zähler in base62: 6 Zeichen reichen für 56,8 Milliarden Links, ohne Kollisionen. Sollen Codes nicht erratbar sein, nimm Zufall mit random_bytes() oder einen gekürzten Hash mit mindestens 8 Zeichen base62 oder base64url. Beides braucht einen UNIQUE-Index und eine Wiederholung bei Kollision.

Ist MD5 in PHP noch zu gebrauchen?

Als Prüfsumme gegen zufällige Fehler und als Cache-Schlüssel ja, für alles mit Sicherheitsbezug nein. Kollisionen lassen sich bei MD5 seit 2004 gezielt erzeugen. Einen Tempovorteil gegenüber SHA-256 gab es in meiner Messung (Intel Core Ultra 9, PHP 8.4.6) nicht: SHA-256 war bei 1 MiB rund fünfmal schneller. Auf anderer Hardware kann das anders sein.

Welchen Hash nehme ich für Passwörter in PHP?

Keinen von hash_algos(). Nimm password_hash($passwort, PASSWORD_ARGON2ID) und prüfe mit password_verify(). Die Funktion setzt Salt und Kosten selbst. Als Untergrenze nennt OWASP für Argon2id 19 MiB Speicher, 2 Iterationen und 1 Thread; die PHP-Standardwerte (64 MiB, 4 Iterationen) liegen darüber.

Wie lang ist ein SHA-256-Hash?

256 Bit, also 32 Byte oder 64 Hex-Zeichen. Mit hash('sha256', $daten, true) bekommst du die 32 Rohbytes, die in der Datenbank nur halb so viel Platz brauchen wie die Hex-Darstellung. In PostgreSQL passt dafür bytea, in MySQL BINARY(32).

Was ist der Unterschied zwischen crc32, crc32b und crc32c in PHP?

hash('crc32b', $s) liefert denselben Wert wie die Funktion crc32($s). hash('crc32', $s) ist die bzip2-Variante mit anderer Byte-Reihenfolge, hash('crc32c', $s) verwendet das Castagnoli-Polynom. Alle drei sind 32 Bit lang und nur zur Fehlererkennung gedacht, nicht zur Sicherheit. Für „Hallo Welt“ ergeben sie drei verschiedene Werte.

Wann reicht ein 32-Bit-Hash nicht mehr?

Sobald viele verschiedene Werte eindeutig bleiben müssen. Ein 32-Bit-Hash kollidiert bei etwa 77.000 zufällig verteilten Werten mit 50 % Wahrscheinlichkeit, ein 64-Bit-Hash wie xxh3 erst bei rund 5 Milliarden. Als alleiniger Schlüssel in einer Tabelle mit Hunderttausenden Zeilen ist crc32 deshalb ungeeignet.

Ist ein gehashter Wert aus einem kleinen Wertebereich, etwa eine IP-Adresse, anonym?

Nein. Bei kleinen Wertebereichen wie IPv4-Adressen lassen sich alle 4,3 Milliarden Möglichkeiten in wenigen Minuten auf einem Kern durchrechnen, auch mit SHA-256. Solche Hashes sind Pseudonyme und bleiben für den Betreiber, der die Zuordnung herstellen kann, personenbezogen (Erwägungsgrund 26 DSGVO).

Wie viele Hash-Verfahren hat PHP?

In PHP 8.4.6 auf Windows listet hash_algos() 60 Verfahren auf. Die Zahl hängt von Version und Build ab. Dazu kommen mit der Erweiterung sodium BLAKE2b (sodium_crypto_generichash()) und SipHash (sodium_crypto_shorthash()). Für neue Anwendungen reichen drei Bausteine: xxh3 für Schlüssel aus eigenen Daten, sha256 mit hash_hmac() für Integrität und Tokens und password_hash() für Passwörter. blake2b ist eine Option, wenn ext-sodium vorhanden ist.


Verwandte Seminare und Artikel


Im Seminar vertiefen

Zu diesem Thema gibt es Seminare und Beratung. Die Agenda von PHP OOP Fortgeschrittene Technologien enthält den Punkt „Performance & Benchmarking: Flaschenhälse im Code vermeiden“, das Seminar KI & VibeCoding für PHP-Entwickler behandelt Audits auf Korrektheit, Sicherheit und Performance. Das Angebot richtet sich an Unternehmen, Behörden und Selbstständige.

Die Anfrage ist unverbindlich und kostenlos; Umfang und Konditionen klären wir danach individuell.

Inhouse-Seminar: PHP OOP Fortgeschrittene Technologien

Die Agenda enthält „Performance & Benchmarking: Flaschenhälse im Code vermeiden“. Das Seminar findet inhouse oder remote statt, bei Bedarf am Code deines Teams.

Zum Seminar

Coaching am Arbeitsplatz

Du willst wissen, welches Verfahren in deiner Anwendung passt oder ob die Kosten deiner Passwort-Hashes stimmen? Wir messen gemeinsam auf deiner Hardware, im Raum Köln und Leverkusen vor Ort, bundesweit inhouse oder remote.

Beratung oder Coaching anfragen