# Jak zabránit XSS v PHP: spolehlivá obrana proti cross-site scriptingu

> Jak zabránit XSS v PHP: kódujte výstupy podle kontextu, používejte šablony s automatickým escapováním a nastavte Content Security Policy. S ukázkami.

Zdroj: https://phpsos.de/cs/ratgeber/xss-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ě:** XSS v PHP zabráníte tak, že každý výstup kódujete podle jeho kontextu: `htmlspecialchars()` pro HTML, `json_encode()` s ochrannými volbami pro JavaScript, ověřené URL pro odkazy. Šablony s automatickým escapováním vám velkou část práce ušetří. Content Security Policy a cookies HttpOnly omezují škodu, pokud nějaká mezera přece zůstane.

## Proč by se cross-site scripting měl kontrolovat také nyní

Pozornost se v říjnu 2026 soustředí na PHP2Shell, dvě kritické zranitelnosti v interpretu PHP, pro které existují záplaty od 21. 10. 2026. Mnoho firem v této souvislosti nechává své webové aplikace přezkoumat. To je dobrá příležitost podívat se i na slabiny ve vlastním kódu, které žádná aktualizace PHP neodstraní.

Cross-site scripting, zkráceně XSS, je jednou z nich. OWASP Top 10 vede XSS v rámci kategorie Injection. Na rozdíl od zranitelnosti na serveru míří útok proti návštěvníkům a uživatelům vašeho webu, například proti zákazníkům v e-shopu nebo zaměstnancům v administraci.

Následky mohou být značné: převzaté relace správců, zmanipulované formuláře odesílající data třetím stranám nebo zfalšovaný obsah na vašem webu. Pokud je převzat účet správce, může to vést až k úplné kontrole nad aplikací.

## Jak XSS v aplikacích PHP vzniká?

XSS vzniká, když aplikace vypisuje data z nedůvěryhodného zdroje do stránky bez kontroly. Prohlížeč pak už nerozliší váš obsah od podstrčeného kódu a provede obojí.

Rozlišují se tři formy. U reflektovaného XSS pochází obsah přímo z aktuálního požadavku, například z hledaného výrazu v URL. U uloženého XSS leží v databázi, například v komentáři nebo poli profilu, a zasáhne každého, kdo stránku zobrazí. XSS založené na DOM vzniká v JavaScriptu prohlížeče, když skripty zapisují hodnoty z URL do stránky.

Nedůvěryhodná jsou všechna data, která nejsou pevně v kódu: pole formulářů, parametry URL, cookies, názvy souborů, odpovědi externích rozhraní a obsah z vlastní databáze, který kdysi přišel zvenčí.

## Správné kódování výstupu podle kontextu

Nejdůležitější pravidlo zní: kódovat při výstupu, a to podle místa, kam hodnota míří. Pro obsah HTML a atributy v uvozovkách je prostředkem volby `htmlspecialchars()`. Malá pomocná funkce zabrání tomu, aby se zapomněly volby:

```php
<?php
function e(?string $wert): string
{
    return htmlspecialchars($wert ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
}
?>
<h2><?= e($produkt['name']) ?></h2>
<input type="text" name="suche" value="<?= e($_GET['q'] ?? '') ?>">
```

Jiné kontexty vyžadují jiné prostředky:

| Kontext | Bezpečná metoda |
|---|---|
| Obsah HTML, atribut v uvozovkách | `htmlspecialchars()` s `ENT_QUOTES` a UTF-8 |
| Data pro JavaScript | `json_encode()` s `JSON_HEX_TAG`, `JSON_HEX_AMP`, `JSON_HEX_APOS`, `JSON_HEX_QUOT` |
| Parametr v URL | `rawurlencode()` pro jednotlivou hodnotu |
| Úplná URL ze vstupu uživatele | Ověřit schéma, povolit jen `https` a `http`, poté `htmlspecialchars()` |
| Atributy bez uvozovek, obslužné rutiny událostí, CSS | vyhnout se, hodnoty tam nedosazovat |

Pro data, která skript potřebuje, je nejčistší cestou datový atribut nebo blok JSON:

```php
<?php
$daten = json_encode($konfiguration, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>
<div id="app" data-konfig="<?= e($daten) ?>"></div>
```

V JavaScriptu pak hodnotu přečtete z datového atributu a obsah nastavujete pomocí `textContent`, nikoli `innerHTML`.

## Šablony, čištění HTML a frameworky

Šablonovací systémy jako Twig a Blade kódují výstupy standardně automaticky. To vylučuje velkou část typických chyb. Nebezpečné je to tam, kde je toto chování záměrně vypnuto, například filtrem `raw` v Twigu nebo syntaxí `{!! !!}` v Bladu. Taková místa by měla být v kódu vzácná a při každém review se cíleně kontrolovat.

Pokud má vaše aplikace záměrně povolit HTML od uživatelů, například formátování v komentářích nebo v redakčním systému, kódování nestačí, protože HTML se má zobrazit. Pak potřebujete ověřenou knihovnu pro čištění, jako je HTML Purifier, která propustí jen pevný seznam povolených prvků a atributů. Vlastní filtry s regulárními výrazy jsou podle zkušeností děravé.

> **Důležité:**
> Zkontrolujte zejména oblasti administrace. XSS v zobrazeních, která vidí jen správci, se často podceňuje, ale zasahuje právě účty s nejvíce oprávněními.

## Jak najít XSS ve stávajícím kódu PHP

V rozrostlých aplikacích není první otázkou většinou to, jak správně kódovat, ale kde se na to dosud zapomnělo. Pragmatickým začátkem je hledání míst, kde se proměnné vypisují přímo:

```bash
# Přímé výpisy dat požadavku
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Vypnuté automatické escapování v Twigu a Bladu
grep -rnE "\|raw|\{!!" templates/ resources/views/
```

Nálezy jsou úkoly ke kontrole, nikoli hotová zjištění. Uložené XSS takové hledání najde jen omezeně, protože hodnoty tam pocházejí z databáze. Zkontrolujte proto navíc všechna zobrazení, která ukazují obsah od uživatelů: komentáře, profily, poznámky k objednávkám, tikety podpory. U CMS a e-shopů do toho výslovně patří pluginy a šablony třetích stran, protože tam sídlí velká část logiky výstupu.

## Druhá linie obrany: CSP a bezpečné cookies

I při pečlivém kódu může být jedno místo přehlédnuto. Dvě opatření pak omezí škodu. Content Security Policy povolí prohlížeči jen skripty z určených zdrojů a zakáže vložené skripty, pokud nejsou výslovně povoleny:

```apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
```

Takovou politiku zavádějte postupně, nejdřív v čistě hlásicím režimu přes `Content-Security-Policy-Report-Only`, protože stávající napojení analytických nebo platebních služeb je většinou nutné doplnit. Další hlavičky popisuje příručka [Bezpečnostní hlavičky pro weby](https://phpsos.de/cs/ratgeber/sicherheits-header/).

Cookies relace by měly navíc nést atribut `HttpOnly`, aby je skripty nemohly číst. Jak zabezpečit relace jako celek, popisuje příručka [Zabezpečení relací v PHP](https://phpsos.de/cs/ratgeber/session-sicherheit-php/). Příbuznou slabinu na straně databáze řeší článek [Jak zabránit SQL injection v PHP](https://phpsos.de/cs/ratgeber/sql-injection-php/).

## Jak konkrétně pomáhá PHP SOS?

- Kontrolujeme šablony a výstupy vaší aplikace na chybějící nebo chybné kódování.
- Nálezy hodnotíme podle dosažitelnosti a dotčených účtů a ukazujeme konkrétní opravy.
- Content Security Policy zavádíme postupně, aniž bychom rušili funkce e-shopu nebo plateb.
- Po opravě znovu testujeme a výsledek zdokumentujeme.

Nezávislou kontrolu popisuje služba [Bezpečnostní audit a penetrační test](https://phpsos.de/cs/leistungen/sicherheitsaudit-penetrationstest/).


## Časté dotazy

**Stačí proti XSS htmlspecialchars?**  
Pro běžný text v obsahu HTML a v atributech v uvozovkách ano, pokud uvedete ENT_QUOTES a znakovou sadu UTF-8. V JavaScriptu, v URL nebo v CSS platí jiná pravidla a tam je třeba vlastní, vhodné kódování.

**Mám vstupy filtrovat při ukládání, nebo při výstupu?**  
Kódujte při výstupu, podle cíle. Vstupy byste měli při příjmu kontrolovat na věrohodnost, například délku a formát. Kdo filtruje jen při ukládání, přehlédne data z jiných zdrojů a ztíží pozdější použití v jiných kontextech.

**K čemu je Content Security Policy?**  
Content Security Policy určuje, z jakých zdrojů smí prohlížeč načítat a spouštět skripty. Může výrazně omezit dopady přehlédnuté zranitelnosti XSS, ale správné kódování nenahrazuje.

**Jak povolím formátování jako tučné písmo nebo odkazy v komentářích?**  
Použijte ověřenou knihovnu pro čištění HTML s whitelistem povolených prvků a atributů. Vlastní regulární výrazy pro odstraňování skriptů jsou náchylné k chybám a neměly by se používat.

## Zdroje

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [php.net: bezpečnostní oznámení](https://www.php.net/)
