Aktuální situace: PHP2Shell. Dvě kritické zranitelnosti PHP (CVSS 9.2) umožňují spustit škodlivý kód bez přihlášení. Záplaty od 21. 10. 2026.Co nyní udělat →

Validace a filtrování vstupů v PHP pomocí filter_var

Jak vývojáři kontrolují vstupy v PHP pomocí filter_var, filter_input_array a seznamů povolených hodnot, které filtry jsou zastaralé a proč validace nenahrazuje prepared statements ani kódování výstupu.

Stav 5 min čteníBezpečný vývoj

Stručně: Vstupy v PHP validujete pomocí filter_var() nebo filter_input_array() a jasných pravidel pro každé pole: typ, rozsah, délka a povolené hodnoty. Co nesedí, se odmítne, ne mlčky přetvoří. Validace je důležitá pro čistá data a menší plochu pro útok, ale nenahrazuje ani prepared statements proti SQL injection, ani kódování podle kontextu proti XSS.

Proč „filtrovat všechno“ není koncepce

V mnoha starších projektech v PHP se na začátku každého souboru nachází smyčka, která všechny hodnoty z $_GET a $_POST pošle přes čisticí funkci. Působí to bezpečně. V praxi to jednou odstraní příliš mnoho, například apostrofy v příjmeních, a jindy příliš málo, protože tatáž funkce má platit pro SQL, HTML i názvy souborů současně.

Smysluplná validace funguje jinak: u každého pole se ptá, co je tam povoleno. Objednané množství je celé číslo mezi 1 a horní hranicí. Pole pro řazení je jedna ze tří pevných hodnot. Datum má určitý formát a leží po jiném datu. Všechno ostatní se odmítne.

OWASP přitom rozlišuje dvě úrovně: syntaktickou kontrolu, zda má hodnota správný tvar, a sémantickou kontrolu, zda dává smysl v kontextu. Obě patří na server; kontroly v prohlížeči slouží jen k pohodlí obsluhy.

Jaké filtry PHP obsahuje

Rozšíření Filter je v PHP standardně součástí. Nejdůležitější validační filtry podle příručky PHP:

Filtr Kontroluje Důležité volby a poznámky
FILTER_VALIDATE_INT celé číslo min_range, max_range
FILTER_VALIDATE_FLOAT desetinné číslo decimal pro oddělovač
FILTER_VALIDATE_BOOL pravdivostní hodnota s FILTER_NULL_ON_FAILURE odlišitelné od „nepravda“
FILTER_VALIDATE_EMAIL tvar e-mailové adresy nedokazuje, že schránka existuje
FILTER_VALIDATE_URL tvar URL schéma ověřit sám, žádné internacionalizované domény
FILTER_VALIDATE_IP adresa IP příznaky pro privátní a rezervované rozsahy
FILTER_VALIDATE_REGEXP vlastní vzor vzor ukotvit pomocí ^ a $

Čisticí filtr FILTER_SANITIZE_STRING je od PHP 8.1 zastaralý. Kdo ho ještě používá, měl by validaci převést na vhodné filtry a pro výstup použít htmlspecialchars().

filter_var v PHP: čistá kontrola formuláře

Pomocí filter_input_array() lze zkontrolovat celý formulář najednou proti definici. Neznámá pole se přitom nepřebírají:

<?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);
    // vypsat chybové hlášení pro každé pole, vstupy nezpracovávat
}

Všimněte si striktních porovnání pomocí ===: platná hodnota 0 nebo prázdný řetězec se nesmí zaměnit s neúspěchem. Proč jsou zde volná porovnání nebezpečná, vysvětluje článek Type juggling: volná porovnání jako bezpečnostní riziko. Jak PHP data požadavku vůbec poskytuje, popisuje stránka Superglobals: $_GET, $_POST, $_FILES a spol..

Úskalí u e-mailu, URL a volných textů

Některé filtry dělají méně, než by název naznačoval:

  1. URL: FILTER_VALIDATE_URL přijímá i jiná schémata než http a https. Schéma poté zkontrolujte pomocí parse_url() proti pevnému seznamu. Pokud server URL stahuje, platí další pravidla, viz Prevence Server-Side Request Forgery v PHP.
  2. E-mailové adresy: Filtr kontroluje tvar, ne vlastnictví. Kdo si potřebuje být jistý, pošle potvrzovací odkaz.
  3. Volný text: Jména, zprávy a adresy lze jen stěží smysluplně omezit na znaky. Kontrolujte délku a kódování znaků, například pomocí mb_check_encoding($wert, 'UTF-8'), a při výstupu se spolehněte na kódování.
  4. Regulární výrazy: Bez ^ a $ vzor kontroluje jen to, zda se vyskytuje někde v textu, ne zda sedí celá hodnota.
  5. Pole místo řetězců: Útočník může místo textu poslat pole. Bez kontroly typu to vede k varováním nebo neočekávanému chování.
Tip

Pro pevné hodnoty výběru, jako je řazení, jazyk nebo způsob dopravy, je seznam povolených hodnot pomocí in_array($wert, $erlaubt, true) nebo výrazu match jednodušší a přísnější než jakýkoli regulární výraz.

Další častá chyba se týká čísel s významem, například cen, množství nebo slev. To, že je hodnota platné číslo, neříká nic o tom, zda je v kontextu povolená. Záporné množství, sleva nad hodnotou zboží nebo cena převzatá z formuláře místo z databáze jsou klasické logické chyby. Ceny, oprávnění a stavové hodnoty proto vždy patří na server a nikdy do skrytých polí formuláře.

JSON, webhooky a importy: validace mimo formuláře

Mnoho vstupů dnes už nejsou formuláře, ale požadavky JSON z aplikací, webhooky platebních služeb nebo importní soubory od dodavatelů. I tam platí: nejdřív bezpečně načíst, pak zkontrolovat. OWASP radí omezit velikost a hloubku vnoření dříve, než se data vůbec načtou. V PHP část toho zajistí json_decode() s maximální hloubkou a JSON_THROW_ON_ERROR:

<?php
$roh = file_get_contents('php://input', false, null, 0, 65536); // nejvýše 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;
}

U webhooků stojí před kontrolou obsahu otázka, zda zpráva skutečně pochází z uvedené služby. Pokud služba nabízí podpis, ověřte ho a porovnejte pomocí hash_equals(). U importních souborů kontrolujte každý řádek jako formulář a při chybách čistě přerušte, místo abyste přebírali poloviční záznamy.

Chybová hlášení by měla jmenovat dotčené pole, odmítnutou hodnotu by ale neměla vracet nefiltrovanou. Jinak vznikne u samotného chybového hlášení nové místo výstupu, které je opět nutné kódovat.

Co validace nedokáže

Validace zmenšuje plochu pro útok, ale podle OWASP není hlavní obranou proti injection. Příjmení s apostrofem je platné a přesto se musí bezpečně dostat do databáze. To se podaří jen pomocí prepared statements, jak ukazuje příručka Prevence SQL injection v PHP. A platný text komentáře se stane neškodným teprve kódováním podle kontextu při výstupu.

Správné pořadí tedy zní: při příjmu validovat, při zpracování parametrizovat, při výstupu kódovat. Navíc před to patří limity velikosti: požadavky, které jsou příliš velké, by webserver a PHP vůbec neměly zpracovávat celé. K tomu slouží nastavení jako post_max_size, které by se po PHP2Shell mělo stejně zkontrolovat.

Jak konkrétně pomáhá PHP SOS?

  • Zaznamenáme všechny vstupy vaší aplikace: formuláře, rozhraní, uploady, importy a webhooky.
  • U každého pole stanovíme, co je povoleno, a plošné čisticí funkce nahradíme jasnými kontrolami.
  • Zkontrolujeme, zda jsou přístupy do databáze a výstupy zabezpečeny nezávisle na validaci.
  • Pravidla zdokumentujeme tak, aby je váš tým mohl u nových funkcí dále uplatňovat.

Systematickou kontrolu popisuje služba Bezpečnostní audit a penetrační test.

Časté dotazy

Jaký je rozdíl mezi validací a čištěním?

Validace ověřuje, zda hodnota odpovídá očekávání, a jinak ji odmítne. Čištění hodnotu mění, například odstraněním znaků. Pro bezpečnostní rozhodnutí je odmítnutí téměř vždy lepší, protože mlčky změněná hodnota může způsobit nové chyby.

Lze FILTER_SANITIZE_STRING ještě používat?

Filtr je od PHP 8.1 označen jako zastaralý. Příručka PHP místo něj doporučuje htmlspecialchars() při výstupu. Stávající kód byste měli upravit, protože zastaralé funkce mohou v budoucích verzích zaniknout.

Stačí FILTER_VALIDATE_URL k zabezpečení odkazů?

Ne. Filtr kontroluje jen tvar adresy. Podle příručky nemusí platná URL začínat http, proto musíte schéma sami ověřit proti seznamu povolených hodnot, například https. Internacionalizované doménové názvy filtr odmítá.

Musím validovat i data z vlastní databáze?

Při výstupu se považují za nedůvěryhodná a vhodně se kódují. Opětovná validace dává smysl, pokud data původně přišla zvenčí a nyní se používají v novém kontextu, například jako název souboru nebo část příkazu.

Pomáhá validace proti PHP2Shell?

Ne. PHP2Shell se týká zpracování požadavků v interpretu PHP dříve, než vůbec začne běžet kód vaší aplikace. Pomáhá jen aktualizace PHP s následným restartem všech procesů PHP.

Zdroje

Okamžitý kontakt

Každá hodina se počítá. Napište nám hned.

Stručně popište systém, verzi PHP a pozorování. Ozveme se, vyjasníme situaci a budeme pracovat na dálku, dokud nebude incident uzavřen a zdokumentován.

Okamžitá pomoc