Kort sagt: Input i PHP validerer I med filter_var() eller filter_input_array() og klare regler pr. felt: type, område, længde og tilladte værdier. Det, der ikke passer, afvises, ikke stiltiende ombygget. Validering er vigtig for rene data og mindre angrebsflade, men erstatter hverken prepared statements mod SQL-injection eller kontekstrigtig kodning mod XSS.
Hvorfor "filtrér alt" ikke er et koncept
I mange ældre PHP-projekter findes i begyndelsen af hver fil en løkke, der sender alle værdier fra $_GET og $_POST gennem en rensefunktion. Det føles sikkert. I praksis fjerner det nogle gange for meget, f.eks. apostrofer i efternavne, og nogle gange for lidt, fordi den samme funktion skal gælde for SQL, HTML og filnavne på én gang.
Fornuftig validering arbejder anderledes: Den spørger for hvert felt, hvad der er tilladt dér. En bestillingsmængde er et helt tal mellem 1 og en øvre grænse. Et sorteringsfelt er en af tre faste værdier. En dato foreligger i et bestemt format og ligger efter en anden dato. Alt andet afvises.
OWASP skelner her mellem to niveauer: den syntaktiske kontrol af, om en værdi har den rigtige form, og den semantiske kontrol af, om den giver mening i sammenhængen. Begge hører hjemme på serveren; kontroller i browseren tjener kun brugervenligheden.
Hvilke filtre PHP medbringer
Filter-udvidelsen er som standard med i PHP. De vigtigste valideringsfiltre ifølge PHP-manualen:
| Filter | Kontrollerer | Vigtige indstillinger og bemærkninger |
|---|---|---|
FILTER_VALIDATE_INT |
helt tal | min_range, max_range |
FILTER_VALIDATE_FLOAT |
decimaltal | decimal for skilletegnet |
FILTER_VALIDATE_BOOL |
sandhedsværdi | kan med FILTER_NULL_ON_FAILURE skelnes fra "falsk" |
FILTER_VALIDATE_EMAIL |
form på en e-mailadresse | beviser ikke, at postkassen findes |
FILTER_VALIDATE_URL |
form på en URL | kontrollér selv skemaet, ingen internationaliserede domæner |
FILTER_VALIDATE_IP |
IP-adresse | flag for private og reserverede områder |
FILTER_VALIDATE_REGEXP |
eget mønster | forankr mønstret med ^ og $ |
Rensefilteret FILTER_SANITIZE_STRING er forældet siden PHP 8.1. Den, der stadig bruger det, bør omstille valideringen til passende filtre og bruge htmlspecialchars() til output.
filter_var i PHP: kontrollér en formular ordentligt
Med filter_input_array() kan en hel formular kontrolleres i ét trin mod en definition. Ukendte felter overtages ikke:
<?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);
// Udskriv fejlmeddelelse pr. felt, behandl ikke input
}
Bemærk de strikte sammenligninger med ===: En gyldig værdi 0 eller en tom streng må ikke forveksles med en fejl. Hvorfor løse sammenligninger er farlige her, forklarer Type juggling: Løse sammenligninger som sikkerhedsrisiko. Hvordan PHP overhovedet stiller forespørgselsdata til rådighed, beskriver Superglobals: $_GET, $_POST, $_FILES og co..
Faldgruber ved e-mail, URL og fri tekst
Nogle filtre gør mindre, end deres navn lader formode:
- URL'er:
FILTER_VALIDATE_URLaccepterer også andre skemaer end http og https. Kontrollér bagefter skemaet medparse_url()mod en fast liste. Hentes URL'en af serveren, gælder yderligere regler, se Forhindr Server-Side Request Forgery i PHP. - E-mailadresser: Filteret kontrollerer formen, ikke ejerskabet. Den, der skal være sikker, sender et bekræftelseslink.
- Fri tekst: Navne, beskeder og adresser lader sig næppe fornuftigt begrænse på tegn. Kontrollér længde og tegnkodning, f.eks. med
mb_check_encoding($wert, 'UTF-8'), og stol ved output på kodning. - Regulære udtryk: Uden
^og$kontrollerer et mønster kun, om det forekommer et sted i teksten, ikke om hele værdien passer. - Arrays i stedet for strenge: En angriber kan sende et array i stedet for en tekst. Uden typekontrol fører det til advarsler eller uventet adfærd.
Til faste valgværdier som sortering, sprog eller forsendelsesmåde er en tilladelsesliste med in_array($wert, $erlaubt, true) eller et match-udtryk enklere og strengere end ethvert regulært udtryk.
En anden hyppig fejl berører tal med betydning, f.eks. priser, mængder eller rabatter. At en værdi er et gyldigt tal, siger intet om, om den er tilladt i sammenhængen. En negativ mængde, en rabat over varens værdi eller en pris, der overtages fra formularen i stedet for fra databasen, er klassiske logikfejl. Priser, rettigheder og statusværdier hører derfor altid hjemme på serveren og aldrig i skjulte formularfelter.
JSON, webhooks og importer: Validering ud over formularer
Mange indgange er i dag ikke længere formularer, men JSON-forespørgsler fra apps, webhooks fra betalingstjenester eller importfiler fra leverandører. Også dér gælder: først indlæs sikkert, så kontrollér. OWASP råder til at begrænse størrelse og indlejringsdybde, før data overhovedet indlæses. I PHP overtager json_decode() med en maksimal dybde og JSON_THROW_ON_ERROR en del af det:
<?php
$roh = file_get_contents('php://input', false, null, 0, 65536); // højst 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;
}
Ved webhooks står spørgsmålet, om meddelelsen virkelig stammer fra den angivne tjeneste, før den indholdsmæssige kontrol. Tilbyder tjenesten en signatur, kontrollerer I den og sammenligner med hash_equals(). Ved importfiler kontrollerer I hver linje som en formular og afbryder rent ved fejl i stedet for at overtage halve datasæt.
Fejlmeddelelser bør nævne det berørte felt, men ikke returnere den afviste værdi ufiltreret. Ellers opstår der ved selve fejlmeddelelsen et nyt outputsted, som igen skal kodes.
Hvad validering ikke kan klare
Validering mindsker angrebsfladen, men er ifølge OWASP ikke hovedforsvaret mod injection. Et efternavn med apostrof er gyldigt og skal alligevel nå sikkert ind i en database. Det lykkes kun med prepared statements, som vejledningen Forhindr SQL-injection i PHP viser. Og en gyldig kommentartekst bliver først ufarlig gennem kontekstrigtig kodning ved output.
Den rigtige rækkefølge er altså: valider ved modtagelsen, parametrisér ved behandlingen, kod ved output. Dertil kommer størrelsesgrænser forud: Forespørgsler, der er langt for store, bør webserver og PHP slet ikke behandle fuldstændigt. Det tjener indstillinger som post_max_size, der efter PHP2Shell alligevel bør kontrolleres.
Hvordan hjælper PHP SOS konkret?
- Vi registrerer alle indgange i jeres applikation: formularer, grænseflader, uploads, importer og webhooks.
- Vi fastlægger pr. felt, hvad der er tilladt, og erstatter generelle rensefunktioner med klare kontroller.
- Vi kontrollerer, om databaseadgange og output er sikret uafhængigt af valideringen.
- Vi dokumenterer reglerne, så jeres team kan føre dem videre ved nye funktioner.
En systematisk kontrol beskriver ydelsen Sikkerhedsaudit og penetrationstest.
Ofte stillede spørgsmål
Hvad er forskellen mellem at validere og at rense?
Validering kontrollerer, om en værdi svarer til forventningerne, og afviser den ellers. Rensning ændrer værdien, f.eks. ved at tegn fjernes. Til sikkerhedsbeslutninger er afvisning næsten altid bedre, fordi en stiltiende ændret værdi kan skabe nye fejl.
Kan FILTER_SANITIZE_STRING stadig bruges?
Filteret er markeret som forældet siden PHP 8.1. PHP-manualen anbefaler i stedet htmlspecialchars() ved output. Eksisterende kode bør I omstille, fordi forældede funktioner kan bortfalde i fremtidige versioner.
Er FILTER_VALIDATE_URL nok til at gøre links sikre?
Nej. Filteret kontrollerer kun adressens form. Ifølge manualen behøver en gyldig URL ikke at begynde med http, så I må selv kontrollere skemaet mod en tilladelsesliste som https. Internationaliserede domænenavne afviser filteret.
Skal jeg også validere data fra min egen database?
Ved output anses de for ikke at være troværdige og kodes passende. En fornyet validering er fornuftig, hvis dataene oprindeligt kom udefra og nu bruges i en ny sammenhæng, f.eks. som filnavn eller del af en kommando.
Hjælper validering mod PHP2Shell?
Nej. PHP2Shell berører behandlingen af forespørgsler i PHP-fortolkeren, før jeres applikationskode overhovedet kører. Mod det hjælper kun PHP-opdateringen med efterfølgende genstart af alle PHP-processer.