Röviden: A bemeneteket PHP-ban a filter_var() vagy a filter_input_array() függvénnyel és mezőnkénti egyértelmű szabályokkal validálja: típus, tartomány, hossz és megengedett értékek. Ami nem felel meg, azt elutasítja, és nem alakítja át csendben. A validálás fontos a tiszta adatokhoz és a kisebb támadási felülethez, de nem helyettesíti sem az SQL-injection elleni prepared statementeket, sem az XSS elleni, az összefüggésnek megfelelő kódolást.
Miért nem koncepció a „mindent szűrni”
Sok régebbi PHP-projektben minden fájl elején található egy ciklus, amely a $_GET és a $_POST összes értékét átküldi egy tisztító függvényen. Ez biztonságosnak érződik. A gyakorlatban hol túl sokat távolít el, például az aposztrófot a vezetéknevekből, hol túl keveset, mert ugyanannak a függvénynek egyszerre kell érvényesnek lennie SQL-re, HTML-re és fájlnevekre.
Az értelmes validálás másképp működik: minden mezőnél azt kérdezi, mi megengedett ott. Egy rendelési mennyiség egy 1 és egy felső határ közötti egész szám. Egy rendezési mező három fix érték egyike. Egy dátum meghatározott formátumú, és egy másik dátum után van. Minden mást elutasít.
Az OWASP két szintet különböztet meg: a szintaktikai ellenőrzést, hogy az érték formája helyes-e, és a szemantikai ellenőrzést, hogy összefüggésében van-e értelme. Mindkettő a szerverre tartozik; a böngészőben végzett ellenőrzések csak a kezelhetőséget szolgálják.
Milyen szűrőket hoz magával a PHP
A szűrő-bővítmény alapból része a PHP-nak. A legfontosabb validálási szűrők a PHP kézikönyve szerint:
| Szűrő | Mit ellenőriz | Fontos opciók és megjegyzések |
|---|---|---|
FILTER_VALIDATE_INT |
egész szám | min_range, max_range |
FILTER_VALIDATE_FLOAT |
tizedes tört | decimal az elválasztó jelhez |
FILTER_VALIDATE_BOOL |
logikai érték | a FILTER_NULL_ON_FAILURE-rel megkülönböztethető a „hamis”-tól |
FILTER_VALIDATE_EMAIL |
az e-mail-cím formája | nem bizonyítja, hogy a postafiók létezik |
FILTER_VALIDATE_URL |
az URL formája | a sémát magának kell ellenőrizni, nincs nemzetköziesített domain |
FILTER_VALIDATE_IP |
IP-cím | flagek a privát és fenntartott tartományokhoz |
FILTER_VALIDATE_REGEXP |
saját minta | a mintát ^-val és $-ral rögzíteni |
A FILTER_SANITIZE_STRING tisztítószűrő a PHP 8.1 óta elavult. Aki még használja, állítsa át a validálást megfelelő szűrőkre, a kimenethez pedig használja a htmlspecialchars() függvényt.
filter_var PHP-ban: egy űrlap rendes ellenőrzése
A filter_input_array() segítségével egy egész űrlap egy lépésben ellenőrizhető egy definícióval szemben. Az ismeretlen mezőket közben nem veszi át:
<?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);
// Mezőnként hibaüzenetet kiírni, a bemeneteket nem feldolgozni
}
Figyeljen a === szigorú összehasonlításokra: egy érvényes 0 érték vagy egy üres string nem téveszthető össze a kudarccal. Hogy a laza összehasonlítások miért veszélyesek itt, azt a Type juggling: laza összehasonlítások mint biztonsági kockázat magyarázza el. Hogy a PHP egyáltalán hogyan teszi elérhetővé a kéréseknek az adatait, azt a Szuperglobálisok: $_GET, $_POST, $_FILES és társaik írja le.
Buktatók az e-mailnél, az URL-nél és a szabad szövegeknél
Néhány szűrő kevesebbet tesz, mint amit a nevük sejtet:
- URL-ek: a
FILTER_VALIDATE_URLa http-től és a https-től eltérő sémákat is elfogad. A sémát utána ellenőrizze aparse_url()segítségével egy rögzített listával szemben. Ha az URL-t a szerver hívja le, további szabályok érvényesek, lásd Server-Side Request Forgery megelőzése PHP-ban. - E-mail-címek: a szűrő a formát ellenőrzi, nem a birtoklást. Aki biztosra akar menni, megerősítő linket küld.
- Szabad szöveg: a neveket, üzeneteket és címeket aligha lehet értelmesen karakterekre korlátozni. Ellenőrizze a hosszt és a karakterkódolást, például az
mb_check_encoding($wert, 'UTF-8')függvénnyel, a kimenetnél pedig hagyatkozzon a kódolásra. - Reguláris kifejezések:
^és$nélkül a minta csak azt ellenőrzi, hogy előfordul-e valahol a szövegben, nem azt, hogy az egész érték megfelel-e. - Tömbök string helyett: a támadó szöveg helyett tömböt küldhet. Típusellenőrzés nélkül ez figyelmeztetésekhez vagy váratlan viselkedéshez vezet.
Fix választási értékeknél, mint a rendezés, a nyelv vagy a szállítási mód, az in_array($wert, $erlaubt, true) vagy egy match-kifejezés segítségével létrehozott engedélyezési lista egyszerűbb és szigorúbb, mint bármely reguláris kifejezés.
Egy további gyakori hiba a jelentéssel bíró számokat érinti, például az árakat, mennyiségeket vagy kedvezményeket. Az, hogy egy érték érvényes szám, semmit sem mond arról, hogy összefüggésében megengedett-e. Egy negatív mennyiség, az áruértéket meghaladó kedvezmény vagy egy ár, amelyet az adatbázis helyett az űrlapból vesznek át, klasszikus logikai hibák. Az árak, jogosultságok és állapotértékek ezért mindig a szerverre tartoznak, és soha nem rejtett űrlapmezőkbe.
JSON, webhookok és importok: validálás az űrlapokon túl
Sok bemenet ma már nem űrlap, hanem alkalmazások JSON-kérése, fizetési szolgáltatók webhookja vagy beszállítók importfájlja. Itt is érvényes: először biztonságosan beolvasni, aztán ellenőrizni. Az OWASP azt tanácsolja, hogy az adatok beolvasása előtt korlátozza a méretet és a beágyazási mélységet. PHP-ban a json_decode() maximális mélységgel és JSON_THROW_ON_ERROR-ral átvállalja ennek egy részét:
<?php
$roh = file_get_contents('php://input', false, null, 0, 65536); // legfeljebb 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;
}
Webhookoknál a tartalmi ellenőrzés előtt az a kérdés áll, hogy az üzenet valóban a megadott szolgáltatótól származik-e. Ha a szolgáltatás aláírást kínál, ellenőrizze, és hasonlítsa össze a hash_equals() függvénnyel. Importfájloknál minden sort úgy ellenőrizzen, mint egy űrlapot, és hiba esetén tisztán szakítsa meg, ahelyett hogy fél adatrekordokat venne át.
A hibaüzenetek nevezzék meg az érintett mezőt, az elutasított értéket viszont ne adják vissza szűretlenül. Különben magán a hibaüzeneten új kimeneti hely keletkezik, amelyet újra kódolni kell.
Amit a validálás nem tud megoldani
A validálás csökkenti a támadási felületet, de az OWASP szerint nem az injection elleni fő védelem. Az aposztrófos vezetéknév érvényes, és mégis biztonságosan kell eljutnia az adatbázisba. Ez csak prepared statementekkel sikerül, ahogyan az SQL-injection megelőzése PHP-ban útmutató mutatja. Egy érvényes hozzászólás szövege pedig csak a kimenetnél alkalmazott, az összefüggésnek megfelelő kódolással válik veszélytelenné.
A helyes sorrend tehát: fogadáskor validálni, feldolgozáskor paraméterezni, kimenetkor kódolni. Ezenkívül elé tartoznak a méretkorlátok: a túl nagy kéréseket a webszervernek és a PHP-nak egyáltalán nem szabad teljesen feldolgoznia. Erre szolgálnak az olyan beállítások, mint a post_max_size, amelyeket a PHP2Shell után amúgy is ellenőrizni kell.
Hogyan segít konkrétan a PHP SOS?
- Felmérjük az alkalmazása összes bemenetét: űrlapokat, interfészeket, feltöltéseket, importokat és webhookokat.
- Mezőnként meghatározzuk, mi megengedett, és az átalánytisztító függvényeket egyértelmű ellenőrzésekre cseréljük.
- Ellenőrizzük, hogy az adatbázis-hozzáférések és a kimenetek a validálástól függetlenül is biztosítva vannak-e.
- A szabályokat úgy dokumentáljuk, hogy a csapata új funkcióknál továbbvihesse őket.
A rendszeres ellenőrzést a biztonsági audit és penetrációs teszt szolgáltatás írja le.
Gyakori kérdések
Mi a különbség a validálás és a tisztítás között?
A validálás azt ellenőrzi, hogy egy érték megfelel-e az elvárásoknak, és ha nem, elutasítja. A tisztítás módosítja az értéket, például karaktereket távolít el. Biztonsági döntéseknél az elutasítás szinte mindig jobb, mert egy csendben módosított érték új hibákat okozhat.
Használható még a FILTER_SANITIZE_STRING?
A szűrőt a PHP 8.1 óta elavultként jelölték meg. A PHP kézikönyve helyette a htmlspecialchars() használatát ajánlja a kimenetnél. A meglévő kódot érdemes átállítani, mert az elavult függvények a jövőbeli verziókban megszűnhetnek.
Elég a FILTER_VALIDATE_URL a linkek biztonságossá tételéhez?
Nem. A szűrő csak a cím formáját ellenőrzi. A kézikönyv szerint egy érvényes URL nem feltétlenül http-vel kezdődik, ezért a sémát Önnek kell ellenőriznie egy engedélyezési listával, például a https-sel. A nemzetköziesített domainneveket a szűrő elutasítja.
A saját adatbázisból származó adatokat is validálni kell?
Kimenetkor nem megbízhatónak számítanak, és megfelelően kódolni kell őket. Az ismételt validálás akkor értelmes, ha az adatok eredetileg kívülről érkeztek, és most új összefüggésben használják őket, például fájlnévként vagy egy parancs részeként.
Segít a validálás a PHP2Shell ellen?
Nem. A PHP2Shell a kérések feldolgozását érinti a PHP-értelmezőben, még mielőtt az Ön alkalmazáskódja egyáltalán futna. Ez ellen csak a PHP-frissítés segít, az összes PHP-folyamat azt követő újraindításával.