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 →

Jak zabránit XSS v PHP: spolehlivá obrana proti cross-site scriptingu

Jak vývojáři zabrání cross-site scriptingu v aplikacích PHP: kódování podle kontextu, automatické escapování v šablonách, čištění HTML a Content Security Policy.

Stav 5 min čteníPoradna

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
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
$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:

# 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:

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.

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. Příbuznou slabinu na straně databáze řeší článek Jak zabránit SQL injection v 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.

Č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

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