# Validace a filtrování vstupů v PHP pomocí filter_var

> Validace vstupů v PHP pomocí filter_var a filter_input_array: čísla, e-mail, URL, bool, seznamy povolených hodnot a limity. S ukázkami kódu.

Zdroj: https://phpsos.de/cs/blog/eingaben-validieren-php/  
Stav: 25.10.2026  
Poskytovatel: PHP SOS, Nouzová pomoc pro PHP a bezpečnost serverů. Okamžitá pomoc přes WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20den%2C%20PHP%20SOS%2C%20pot%C5%99ebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20PHP%20serverem.%20Firma%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**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
<?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](https://phpsos.de/cs/blog/type-juggling-php/). Jak PHP data požadavku vůbec poskytuje, popisuje stránka [Superglobals: $_GET, $_POST, $_FILES a spol.](https://phpsos.de/cs/php/superglobals/).

## Ú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](https://phpsos.de/cs/blog/ssrf-in-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
<?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](https://phpsos.de/cs/ratgeber/sql-injection-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](https://phpsos.de/cs/leistungen/sicherheitsaudit-penetrationstest/).


## Č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

- [Příručka PHP: konstanty filtrů](https://www.php.net/manual/de/filter.constants.php)
- [OWASP Input Validation Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
