Kurz gesagt: Eingaben in PHP validieren Sie mit filter_var() oder filter_input_array() und klaren Regeln je Feld: Typ, Bereich, Länge und erlaubte Werte. Was nicht passt, wird abgelehnt, nicht stillschweigend umgebaut. Validierung ist wichtig für saubere Daten und weniger Angriffsfläche, ersetzt aber weder Prepared Statements gegen SQL-Injection noch kontextgerechte Kodierung gegen XSS.
Warum „alles filtern“ kein Konzept ist
In vielen älteren PHP-Projekten findet sich am Anfang jeder Datei eine Schleife, die alle Werte aus $_GET und $_POST durch eine Bereinigungsfunktion schickt. Das fühlt sich sicher an. In der Praxis entfernt es mal zu viel, etwa Apostrophe in Nachnamen, und mal zu wenig, weil dieselbe Funktion für SQL, HTML und Dateinamen gleichzeitig gelten soll.
Sinnvolle Validierung arbeitet anders: Sie fragt für jedes Feld, was dort erlaubt ist. Eine Bestellmenge ist eine ganze Zahl zwischen 1 und einer Obergrenze. Ein Sortierfeld ist einer von drei festen Werten. Ein Datum liegt in einem bestimmten Format vor und nach einem anderen Datum. Alles andere wird abgelehnt.
OWASP unterscheidet dabei zwei Ebenen: die syntaktische Prüfung, ob ein Wert die richtige Form hat, und die semantische Prüfung, ob er im Zusammenhang Sinn ergibt. Beide gehören auf den Server; Prüfungen im Browser dienen nur der Bedienbarkeit.
Welche Filter PHP mitbringt
Die Filter-Erweiterung ist in PHP standardmäßig enthalten. Die wichtigsten Validierungsfilter laut PHP-Handbuch:
| Filter | Prüft | Wichtige Optionen und Hinweise |
|---|---|---|
FILTER_VALIDATE_INT |
ganze Zahl | min_range, max_range |
FILTER_VALIDATE_FLOAT |
Kommazahl | decimal für das Trennzeichen |
FILTER_VALIDATE_BOOL |
Wahrheitswert | mit FILTER_NULL_ON_FAILURE unterscheidbar von „falsch“ |
FILTER_VALIDATE_EMAIL |
Form einer E-Mail-Adresse | beweist nicht, dass das Postfach existiert |
FILTER_VALIDATE_URL |
Form einer URL | Schema selbst prüfen, keine internationalisierten Domains |
FILTER_VALIDATE_IP |
IP-Adresse | Flags für private und reservierte Bereiche |
FILTER_VALIDATE_REGEXP |
eigenes Muster | Muster mit ^ und $ verankern |
Der Bereinigungsfilter FILTER_SANITIZE_STRING ist seit PHP 8.1 veraltet. Wer ihn noch nutzt, sollte die Validierung auf passende Filter umstellen und für die Ausgabe htmlspecialchars() verwenden.
filter_var in PHP: ein Formular sauber prüfen
Mit filter_input_array() lässt sich ein ganzes Formular in einem Schritt gegen eine Definition prüfen. Unbekannte Felder werden dabei nicht übernommen:
<?php
declare(strict_types=1);
$regeln = [
'menge' => ['filter' => FILTER_VALIDATE_INT,
'options' => ['min_range' => 1, 'max_range' => 50]],
'email' => FILTER_VALIDATE_EMAIL,
'agb' => ['filter' => FILTER_VALIDATE_BOOL, 'flags' => FILTER_NULL_ON_FAILURE],
'versand' => ['filter' => FILTER_VALIDATE_REGEXP,
'options' => ['regexp' => '/^(standard|express)$/']],
];
$daten = filter_input_array(INPUT_POST, $regeln) ?? [];
$fehler = [];
foreach ($regeln as $feld => $_) {
if (!array_key_exists($feld, $daten) || $daten[$feld] === false || $daten[$feld] === null) {
$fehler[] = $feld;
}
}
if ($fehler !== []) {
http_response_code(422);
// Fehlermeldung je Feld ausgeben, Eingaben nicht verarbeiten
}
Beachten Sie die strikten Vergleiche mit ===: Ein gültiger Wert 0 oder ein leerer String darf nicht mit einem Fehlschlag verwechselt werden. Warum lose Vergleiche hier gefährlich sind, erklärt Type Juggling: Lose Vergleiche als Sicherheitsrisiko. Wie PHP Anfragedaten überhaupt bereitstellt, beschreibt Superglobals: $_GET, $_POST, $_FILES und Co..
Fallstricke bei E-Mail, URL und freien Texten
Einige Filter tun weniger, als ihr Name vermuten lässt:
- URLs:
FILTER_VALIDATE_URLakzeptiert auch andere Schemata als http und https. Prüfen Sie das Schema danach mitparse_url()gegen eine feste Liste. Wird die URL vom Server abgerufen, gelten zusätzliche Regeln, siehe Server-Side Request Forgery in PHP verhindern. - E-Mail-Adressen: Der Filter prüft die Form, nicht den Besitz. Wer sicher sein muss, schickt einen Bestätigungslink.
- Freitext: Namen, Nachrichten und Adressen lassen sich kaum sinnvoll auf Zeichen beschränken. Prüfen Sie Länge und Zeichenkodierung, etwa mit
mb_check_encoding($wert, 'UTF-8'), und verlassen Sie sich bei der Ausgabe auf Kodierung. - Reguläre Ausdrücke: Ohne
^und$prüft ein Muster nur, ob es irgendwo im Text vorkommt, nicht ob der ganze Wert passt. - Arrays statt Strings: Ein Angreifer kann statt eines Textes ein Array senden. Ohne Typprüfung führt das zu Warnungen oder unerwartetem Verhalten.
Für feste Auswahlwerte wie Sortierung, Sprache oder Versandart ist eine Erlaubnisliste mit in_array($wert, $erlaubt, true) oder einem match-Ausdruck einfacher und strenger als jeder reguläre Ausdruck.
Ein weiterer häufiger Fehler betrifft Zahlen mit Bedeutung, etwa Preise, Mengen oder Rabatte. Dass ein Wert eine gültige Zahl ist, sagt nichts darüber, ob er im Zusammenhang erlaubt ist. Eine negative Menge, ein Rabatt über dem Warenwert oder ein Preis, der aus dem Formular statt aus der Datenbank übernommen wird, sind klassische Logikfehler. Preise, Berechtigungen und Statuswerte gehören deshalb immer auf den Server und nie in versteckte Formularfelder.
JSON, Webhooks und Importe: Validierung jenseits von Formularen
Viele Eingänge sind heute keine Formulare mehr, sondern JSON-Anfragen von Apps, Webhooks von Zahlungsdiensten oder Importdateien von Lieferanten. Auch dort gilt: erst sicher einlesen, dann prüfen. OWASP rät, Größe und Verschachtelungstiefe zu begrenzen, bevor Daten überhaupt eingelesen werden. In PHP übernimmt json_decode() mit einer maximalen Tiefe und JSON_THROW_ON_ERROR einen Teil davon:
<?php
$roh = file_get_contents('php://input', false, null, 0, 65536); // höchstens 64 KB
try {
$daten = json_decode($roh, true, 16, JSON_THROW_ON_ERROR);
} catch (JsonException) {
http_response_code(400);
exit;
}
if (!is_array($daten) || !is_string($daten['bestellnummer'] ?? null)) {
http_response_code(422);
exit;
}
Bei Webhooks steht vor der inhaltlichen Prüfung die Frage, ob die Nachricht wirklich vom angegebenen Dienst stammt. Bietet der Dienst eine Signatur an, prüfen Sie diese und vergleichen sie mit hash_equals(). Bei Importdateien prüfen Sie jede Zeile wie ein Formular und brechen bei Fehlern sauber ab, statt halbe Datensätze zu übernehmen.
Fehlermeldungen sollten das betroffene Feld nennen, den abgelehnten Wert aber nicht ungefiltert zurückgeben. Sonst entsteht an der Fehlermeldung selbst eine neue Ausgabestelle, die wieder kodiert werden muss.
Was Validierung nicht leisten kann
Validierung verkleinert die Angriffsfläche, aber sie ist laut OWASP nicht die Hauptverteidigung gegen Injection. Ein Nachname mit Apostroph ist gültig und muss trotzdem sicher in eine Datenbank gelangen. Das gelingt nur mit Prepared Statements, wie der Ratgeber SQL-Injection in PHP verhindern zeigt. Und ein gültiger Kommentartext wird erst durch kontextgerechte Kodierung bei der Ausgabe ungefährlich.
Die richtige Reihenfolge lautet also: beim Empfang validieren, bei der Verarbeitung parametrisieren, bei der Ausgabe kodieren. Zusätzlich gehören Größengrenzen davor: Anfragen, die viel zu groß sind, sollten Webserver und PHP gar nicht erst vollständig verarbeiten. Dazu dienen Einstellungen wie post_max_size, die nach PHP2Shell ohnehin überprüft werden sollten.
Wie hilft PHP SOS konkret?
- Wir erfassen alle Eingänge Ihrer Anwendung: Formulare, Schnittstellen, Uploads, Importe und Webhooks.
- Wir legen je Feld fest, was erlaubt ist, und ersetzen pauschale Bereinigungsfunktionen durch klare Prüfungen.
- Wir prüfen, ob Datenbankzugriffe und Ausgaben unabhängig von der Validierung abgesichert sind.
- Wir dokumentieren die Regeln so, dass Ihr Team sie bei neuen Funktionen weiterführen kann.
Eine systematische Prüfung beschreibt die Leistung Sicherheitsaudit und Penetrationstest.
Häufige Fragen
Was ist der Unterschied zwischen Validieren und Bereinigen?
Validieren prüft, ob ein Wert den Erwartungen entspricht, und lehnt ihn sonst ab. Bereinigen verändert den Wert, etwa indem Zeichen entfernt werden. Für Sicherheitsentscheidungen ist Ablehnen fast immer besser, weil ein stillschweigend veränderter Wert neue Fehler erzeugen kann.
Ist FILTER_SANITIZE_STRING noch nutzbar?
Der Filter ist seit PHP 8.1 als veraltet markiert. Das PHP-Handbuch empfiehlt stattdessen htmlspecialchars() bei der Ausgabe. Bestehenden Code sollten Sie umstellen, weil veraltete Funktionen in künftigen Versionen entfallen können.
Reicht FILTER_VALIDATE_URL, um Links sicher zu machen?
Nein. Der Filter prüft nur die Form der Adresse. Laut Handbuch muss eine gültige URL nicht mit http beginnen, deshalb müssen Sie das Schema selbst auf eine Erlaubnisliste wie https prüfen. Internationalisierte Domainnamen lehnt der Filter ab.
Muss ich auch Daten aus der eigenen Datenbank validieren?
Bei der Ausgabe gelten sie als nicht vertrauenswürdig und werden passend kodiert. Eine erneute Validierung ist sinnvoll, wenn die Daten ursprünglich von außen kamen und jetzt in einem neuen Zusammenhang verwendet werden, etwa als Dateiname oder Teil eines Befehls.
Hilft Validierung gegen PHP2Shell?
Nein. PHP2Shell betrifft die Verarbeitung von Anfragen im PHP-Interpreter, bevor Ihr Anwendungscode überhaupt läuft. Dagegen hilft nur das PHP-Update mit anschließendem Neustart aller PHP-Prozesse.