Kurz gesagt: PHP bringt für die häufigsten Sicherheitsaufgaben fertige, geprüfte Funktionen mit: password_hash() für Passwörter, random_bytes() und random_int() für Tokens, htmlspecialchars() gegen Cross-Site-Scripting, vorbereitete Abfragen mit PDO gegen SQL-Injection, hash_equals() für sichere Vergleiche und die Sodium-Erweiterung für Kryptografie. Wer diese Bausteine konsequent nutzt, vermeidet die meisten Anwendungslücken. Gegen Fehler im PHP-Interpreter selbst, wie bei PHP2Shell, helfen sie nicht; dort hilft nur das Update.
Warum eingebaute PHP Sicherheitsfunktionen?
Sicherheitscode selbst zu schreiben ist fehleranfällig. Ein eigener Passwort-Hash, ein selbst gebauter Token-Generator oder ein selbst gestrickter Filter für HTML-Ausgaben sehen oft richtig aus und sind es trotzdem nicht. Die eingebauten Funktionen sind dokumentiert, getestet und werden mit PHP-Updates gepflegt.
Für Entscheider heißt das: Fragen Sie Ihre Agentur oder Ihr Entwicklungsteam nicht, ob Sicherheitsfunktionen selbst gebaut wurden, sondern ob die Standardfunktionen der Sprache genutzt werden. Die Tabelle gibt einen Überblick.
| Aufgabe | Funktion | Verfügbar seit | Wichtiger Hinweis |
|---|---|---|---|
| Passwörter speichern | password_hash(), password_verify() |
PHP 5.5 | Spalte bis 255 Zeichen; bcrypt kürzt auf 72 Bytes |
| Zufällige Tokens | random_bytes(), random_int() |
PHP 7.0 | nie rand() oder mt_rand() für Geheimnisse |
| Zufall objektorientiert | Random\Randomizer |
PHP 8.2 | gleiche Sicherheit, flexiblere Schnittstelle |
| HTML-Ausgabe maskieren | htmlspecialchars() |
seit jeher | Standard-Flags seit 8.1 maskieren auch einfache Anführungszeichen |
| Zeitkonstanter Vergleich | hash_equals() |
PHP 5.6 | Benutzereingabe als zweites Argument |
| Moderne Kryptografie | Sodium (sodium_*) |
PHP 7.2 | statt eigener Verschlüsselung |
| Geheimnisse in Stacktraces | #[\SensitiveParameter] |
PHP 8.2 | Wert erscheint nicht im Trace |
| Eingaben prüfen | filter_var() |
PHP 5.2 | prüft Format, ersetzt keine Maskierung |
Passwörter und Tokens
password_hash() erzeugt einen gesalzenen Hash, password_verify() prüft ihn. Mit PASSWORD_DEFAULT nutzt PHP derzeit bcrypt; seit PHP 8.4 liegt der Standard-Kostenfaktor laut Handbuch bei 12 statt 10. Weil sich der Standardalgorithmus ändern kann, empfiehlt das Handbuch eine Datenbankspalte mit 255 Zeichen. Bei bcrypt werden Passwörter nach 72 Bytes abgeschnitten, was bei sehr langen Passphrasen zu bedenken ist.
<?php
// Registrierung
$hash = password_hash($passwort, PASSWORD_DEFAULT);
// Anmeldung, inklusive automatischer Aufwertung alter Hashes
if (password_verify($eingabe, $hash)) {
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$hash = password_hash($eingabe, PASSWORD_DEFAULT); // neu speichern
}
}
// Token für einen Link zum Zurücksetzen des Passworts
$token = bin2hex(random_bytes(32));
Für Vergleiche von Geheimnissen, etwa einer Signatur, gehört hash_equals() verwendet. Ein normaler Vergleich mit === kann laut Handbuch über die Laufzeit verraten, ab welcher Stelle sich zwei Werte unterscheiden. Ausführlicher: Passwörter in PHP sicher speichern und sichere Zufallszahlen und Tokens.
Ausgaben maskieren und Datenbank sicher abfragen
Cross-Site-Scripting entsteht, wenn Benutzereingaben ungefiltert in HTML landen. htmlspecialchars() wandelt Sonderzeichen in HTML-Entitäten um. Seit PHP 8.1 sind die Standard-Flags ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401; vorher wurden einfache Anführungszeichen nicht umgewandelt. Wer ältere Versionen unterstützen muss, gibt die Flags ausdrücklich an.
SQL-Injection verhindern vorbereitete Abfragen. Die Werte werden getrennt vom SQL-Text übertragen und können die Abfrage nicht verändern:
<?php
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE email = :email');
$stmt->execute(['email' => $email]);
$kunde = $stmt->fetch();
echo htmlspecialchars($kunde['name'] ?? '', ENT_QUOTES, 'UTF-8');
Die Details stehen unter Cross-Site-Scripting in PHP verhindern und in der Seite zu SQL-Injection.
Eingaben zu prüfen ist ein zweiter, getrennter Schritt. filter_var() stellt fest, ob ein Wert ein gültiges Format hat, etwa eine E-Mail-Adresse oder eine Ganzzahl in einem erlaubten Bereich. Das ersetzt aber keine Maskierung bei der Ausgabe und keine vorbereitete Abfrage, denn auch formal gültige Werte können Sonderzeichen enthalten.
Geheimnisse aus Fehlermeldungen heraushalten
Ein unterschätztes Datenleck sind Stacktraces. Tritt in einer Funktion ein Fehler auf, protokolliert PHP die Aufrufkette samt Argumenten. Steht darin ein Passwort oder API-Schlüssel, landet er im Log. Seit PHP 8.2 markiert das Attribut #[\SensitiveParameter] solche Parameter; ihr Wert erscheint im Trace dann nur als Objekt SensitiveParameterValue.
<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
// ... Prüfung ...
throw new RuntimeException('Datenbank nicht erreichbar');
}
Laut einem Hinweis im Handbuch wirkt das Attribut nur an der konkreten Implementierung, nicht an der Methode eines Interfaces. Setzen Sie es deshalb dort, wo der Code tatsächlich läuft.
Sessions: sichere Einstellungen und neue Standards
Sitzungen sind ein häufiges Angriffsziel. Drei Einstellungen sind zentral: session.use_strict_mode=1 lehnt Session-IDs ab, die der Server nicht selbst erzeugt hat; session.cookie_httponly=1 verhindert den Zugriff per JavaScript auf das Session-Cookie; session.cookie_samesite=Lax erschwert das Mitsenden bei fremden Seiten. Laut angenommenem RFC werden genau diese drei Werte mit PHP 8.6 zum Standard. Setzen Sie sie bis dahin selbst, und auch danach ausdrücklich, damit die Konfiguration nicht vom Standard abhängt.
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1
Mehr dazu unter Sessions in PHP absichern.
Was eingebaute Funktionen nicht leisten
Das PHP-Projekt ist in seiner Sicherheitsrichtlinie deutlich: PHP ist keine Sandbox. Umgehungen von open_basedir oder disable_functions, bösartige Eingaben an unserialize() und ungeprüfte Argumente an Funktionen wie mysqli_query() gelten nicht als Sicherheitslücken von PHP. Das heißt für Betreiber: Diese Einstellungen sind nützliche Härtung, aber keine Sicherheitsgrenze, und unserialize() hat mit fremden Daten nichts zu tun.
Der zweite Punkt betrifft PHP2Shell. CVE-2026-109618 und CVE-2026-109619 ermöglichen laut php.net Codeausführung, bevor oder während Ihre Anwendung läuft; bei mod_php wurde ein Exploit gezeigt, bevor überhaupt Anwendungscode ausgeführt wird. Keine Funktion in Ihrem Code kann das abfangen. Sichere Programmierung und aktuelle PHP-Version sind zwei getrennte Pflichten, und beide müssen erfüllt sein.
Wie hilft PHP SOS konkret?
Im Sicherheitsaudit für PHP-Anwendungen prüfen wir gezielt:
- Passwort- und Token-Handling: Werden
password_hash()undrandom_bytes()genutzt, oder gibt es Eigenbauten? - Ausgaben und Abfragen: Wo landen Eingaben ungefiltert in HTML, SQL oder Shell-Befehlen?
- Session-Konfiguration: Sind Strict Mode, HttpOnly, SameSite und Secure gesetzt und nicht überschreibbar?
- Laufzeit: Läuft eine unterstützte PHP-Version mit den Korrekturen vom 21.10.2026?
Häufige Fragen
Wie speichert man Passwörter in PHP richtig?
Mit password_hash() und PASSWORD_DEFAULT, geprüft wird mit password_verify(). Die Datenbankspalte sollte laut Handbuch bis zu 255 Zeichen fassen, weil sich der Standardalgorithmus künftig ändern kann.
Ist rand() für Tokens geeignet?
Nein. Für Tokens, Schlüssel und Passwort-Links gehören random_bytes() oder random_int() verwendet. Sie liefern laut Handbuch kryptografisch sichere Zufallswerte.
Seit wann ist Sodium in PHP enthalten?
Die Sodium-Erweiterung für moderne Kryptografie ist seit PHP 7.2.0 Bestandteil von PHP. Ältere Versionen konnten sie nur über PECL nachrüsten.
Schützen diese Funktionen vor PHP2Shell?
Nein. Sie schützen vor Fehlern in Ihrer Anwendung. PHP2Shell steckt im Interpreter selbst und wird nur durch ein PHP-Update geschlossen, gefolgt vom Neustart aller PHP-Prozesse.
Ändern sich die Session-Standards in PHP 8.6?
Ja, laut angenommenem RFC werden session.use_strict_mode und session.cookie_httponly standardmäßig eingeschaltet und session.cookie_samesite auf Lax gesetzt. Bestehende Konfigurationen sollten Sie trotzdem selbst ausdrücklich setzen.