HTML zapisuje obsah i strukturu do stejného textového proudu. Znak menší než může být součástí matematické věty, ale pro parser také otevírá tag. Ampersand může být běžným znakem názvu firmy, zároveň však zahajuje character reference. HTML entity, přesněji znakové reference, umožňují autorovi říci, že určitý znak má být výsledným textem a nikoli součástí syntaxe dokumentu. Nejsou tajným kódem ani obecným bezpečnostním filtrem. Jsou mechanismem gramatiky, který udržuje hranici mezi daty a markupem.

Parser rozhoduje podle aktuálního kontextu

Mezi tagy prohlížeč interpretuje < jako možný začátek elementu a & jako začátek reference. Zdroj &lt; vytvoří v DOM skutečný znak menší než a &amp; ampersand. Po parsování už DOM obvykle neuchovává, zda byl znak zapsán přímo, číselnou referencí nebo pojmenovanou entitou.

Uvnitř atributu jsou navíc významné uvozovky, které hodnotu ukončují. JavaScript, CSS a URL mají vlastní gramatiky. Jedna univerzální funkce „escape everything“ proto nemůže být správná pro všechna místa v dokumentu.

UTF-8 snížilo potřebu pojmenovaných entit

Historicky byly entity praktické, když zdrojový soubor nebo klávesnice neuměly snadno reprezentovat určitý symbol. Moderní UTF-8 dovoluje většinu znaků zapsat přímo a bývá čitelnější ponechat české či jiné Unicode znaky v přirozené podobě. Reference zůstávají nutné pro syntakticky významné znaky a užitečné pro neviditelné mezery či symbol, jehož ASCII zápis jasněji vyjadřuje záměr.

Automatická konverze každého ne-ASCII znaku do entity nezvyšuje bezpečnost. Pouze zvětšuje zdroj a komplikuje čtení. Cílem je správné chování parseru, ne maximální počet referencí.

Encoding a sanitization řeší odlišné úlohy

Escaping převádí text tak, aby se zobrazil jako text. Když uživatel napíše <strong>, správně escapovaný výstup ukáže tyto znaky a nevytvoří element. Sanitizer je potřeba pouze tehdy, když produkt úmyslně dovoluje část uživatelského HTML, například odstavce, odkazy a zvýraznění.

Sanitizer markup parsuje a podle allowlistu odstraňuje zakázané elementy, atributy a URL schemes. Regex nebo několik string replacements nedokáže spolehlivě modelovat tolerantní HTML parser. Pokud rich text není požadavek, obyčejný text s automatickým escapingem je jednodušší a bezpečnější.

Dvojité kódování odhaluje nejasné vlastnictví

Jestli backend uloží &lt; a template znovu escapuje ampersand, prohlížeč zobrazí text entity místo zamýšleného znaku. Podobné artefakty vznikají, když databáze, API i prezentační vrstva předpokládají, že právě ony mají připravit HTML.

Obvyklé pravidlo je ukládat původní sémantický Unicode text a escapovat až na poslední výstupní hranici. Encoded HTML tvar se nemá zapisovat zpět do databáze jako běžná editace. Výjimkou je pole, jehož deklarovaným datovým typem je sanitizované HTML.

Dekódování může znovu vytvořit nebezpečný markup

Decode je správný, když vstupní smlouva výslovně říká, že pole obsahuje HTML character references. Aplikovat jej obecně na uživatelský text je riskantní. Viditelná sekvence, kterou člověk zamýšlel doslova, se změní na syntaktický znak. Opakované dekódování nested hodnot může postupně vytvořit tag nebo atribut, který dřívější kontrola neviděla.

Transformace musí být jednorázová, dokumentovaná a následovaná správným zacházením s výsledným typem. Decode není nástroj pro náhodné „čištění“ textu.

Unicode normalizace je samostatný problém

Přímý znak, číselná reference a pojmenovaná entita mohou po parsování vytvořit stejný code point. Současně mohou dvě vizuálně stejné podoby Unicode používat jinou sekvenci code points, například složené písmeno nebo základ a kombinující znaménko. HTML entity tento rozdíl neřeší.

Vyhledávání, unikátnost a bezpečnost identifikátorů mohou potřebovat explicitní Unicode normalizaci podle domény. Ta má proběhnout odděleně od output escaping, aby se nemíchal význam dat s gramatikou HTML.

Browser je tolerantní, filtr nesmí hádat

HTML parser obsahuje rozsáhlé zotavení z chyb. Neúplná reference, chybějící uvozovka nebo špatně vnořený element mohou být interpretovány jinak, než očekává jednoduchý serverový filtr. Útočníci využívají rozdíl mezi tím, co odstraní filtr, a tím, co nakonec sestaví prohlížeč.

Produkce má používat udržovaný contextual encoder a sanitizer ověřený proti browser modelu. Content Security Policy přidává další bariéru, ale nenahrazuje správné oddělení textu od kódu.

Template má učinit bezpečnou cestu výchozí

Kvalitní template engine automaticky escapuje běžnou interpolaci a raw HTML vyžaduje explicitní operaci. Review se pak soustředí na několik výjimek: raw helpery, custom filtry a hodnoty sestavené před vstupem do šablony. Každá výjimka má mít známý původ a sanitizer.

Bezpečný encoder už nepomůže, pokud aplikace nejprve spojí nedůvěryhodný text s HTML stringem a výsledku přiřadí status „trusted“. Typy a API mají tuto změnu důvěry zviditelnit.

Zdroj a DOM ukazují různé fáze

Při ladění je potřeba zkontrolovat raw HTTP response i výsledný DOM. Zdroj ukáže, jakou referenci server odeslal; DOM ukáže, jak ji browser interpretoval. DevTools zobrazení elementu může serializovat strukturu jinak než původní response, takže samotný inspector někdy skryje místo dvojitého escapingu.

Regresní test by měl zachytit obě fáze. Kontrola response odhalí chybu serverového encoderu, zatímco browser test ukáže, zda klientský JavaScript hodnotu později znovu neparsuje nebo nedekóduje. U historického obsahu je vhodné přidat skutečné problematické vzorky v anonymizované podobě, protože tolerantní parser může okrajový zápis interpretovat jinak než moderní čistý fixture.

HTML entity jsou přesný syntaktický nástroj. Praktické pravidlo zní: zachovat sémantický text, escapovat pro konkrétní výstupní kontext a sanitizovat pouze záměrně povolený markup. Tím se současně omezí viditelné artefakty i nebezpečné překročení hranice mezi daty a strukturou.