HTML використовує ті самі текстові символи і для змісту сторінки, і для опису її структури. Знак «менше» може бути частиною математичного речення, але для browser parser він також починає tag. Ampersand може бути звичайним символом назви компанії, але водночас відкриває character reference. Entities, точніше HTML character references, дають автору спосіб однозначно сказати: цей символ потрібно показати, а не трактувати як команду markup.

Text і markup проходять одним каналом

Під час читання документа browser визначає значення символів залежно від поточного стану parser. У text node послідовність < створює literal less-than, а & — ampersand. Named references на кшталт © зручні для відомих символів, а numeric references вказують Unicode code point.

Сучасний UTF-8 дозволяє записувати більшість символів безпосередньо. Entities не є обов’язковим способом представлення кожної не-ASCII літери. Вони залишаються потрібними там, де символ має синтаксичне значення або source representation повинно чітко передати намір.

Правильне escaping залежить від місця вставки

Текст між tags і значення в quoted attribute є різними контекстами. Усередині attribute важливими стають quotes, у URL діють власні правила, а JavaScript і CSS мають окремі grammar. Одна універсальна функція «escape HTML» не може безпечно обслуговувати всі ці місця.

Template engine повинен знати output context і автоматично вибирати encoder. Найризикованіші помилки виникають, коли application склеює markup рядками або переносить already-escaped value в інший контекст, де попередня обробка більше не захищає.

Encoding і sanitization вирішують різні задачі

Encoding перетворює введене значення на безпечний text. Якщо користувач написав <strong>, сторінка показує ці символи, а не створює bold element. Sanitization потрібна тоді, коли продукт свідомо дозволяє частину user-authored HTML. Sanitizer parse-ить структуру й видаляє заборонені elements, attributes та URL schemes.

Коли rich text не потрібен, звичайне escaping є простішим і надійнішим. Декодування entities перед raw rendering може повернути небезпечний markup і знищити захист, який існував на попередньому етапі.

Double encoding показує нечітку відповідальність

Якщо encoded ampersand обробити ще раз, &lt; перетвориться на &amp;lt;, і користувач побачить текст entity замість символу. Зазвичай це означає, що database, API та template одночасно вважають себе відповідальними за presentation escaping.

Для звичайних полів краще зберігати semantic Unicode text і escape його на фінальному output boundary. Presentation-specific representation не повинно ставати canonical database value без чіткої причини та окремого типу даних.

Entity не змінює Unicode-значення

Прямий UTF-8 character, decimal reference, hexadecimal reference і named entity можуть створити той самий символ у DOM. Після parsing browser працює із character, а не з тим, як він був записаний у source. Саме тому порівняння сирих HTML strings може відрізнятися від порівняння видимого змісту.

Unicode normalization є окремим питанням. Візуально однакові слова іноді складаються з різних code-point sequences, і заміна символів на entities не вирішує цю різницю.

Browser пробачає помилки, security filter не має права

HTML parser містить складні правила recovery для malformed documents. Неповна або неоднозначна reference може інтерпретуватися не так, як очікує простий string replacement. Атакувальники використовують розбіжності між filter і реальним browser parsing.

Тому security-sensitive code має використовувати зрілі encoders та sanitizers. Content Security Policy додає ще один layer, але не замінює правильний contextual escaping.

Безпечний шлях повинен бути стандартним

Якісний template engine автоматично escape-ить звичайну interpolation, а raw HTML робить помітним винятком. Code review тоді зосереджується на raw-output helpers, custom filters і значеннях, які були зібрані в markup до передачі template.

Entities не є encryption і не приховують дані. Це синтаксичний інструмент, який зберігає межу між content та structure. Практична модель проста: зберігати semantic text, escape для точного output context і sanitize лише явно дозволений markup.

Source і DOM показують різні етапи

Під час debugging корисно порівнювати HTTP response source з parsed DOM у developer tools. Source показує spelling, яке надіслав server: прямий character, named entity або numeric reference. DOM показує структуру й characters після того, як browser застосував parsing та error recovery.

Якщо символ правильний у source, але структура несподівана в DOM, проблема часто полягає в контексті або malformed markup. Якщо неправильне значення вже прийшло з API, виправлення потрібно шукати раніше в pipeline, а не в CSS чи browser rendering.

Entities впливають на довговічність контенту

Контент проходить через editors, migrations, feeds і search indexes. Кожен компонент, який без потреби decode-ить або encode-ить entities, може змінити source representation. Після кількох round trips текст стає складно відрізнити від markup, а видимі artifacts потрапляють у пошук.

Контракт між системами повинен називати тип поля. Plain text передається як text, sanitized HTML — як окремий markup field. Така проста дисципліна важливіша за спробу автоматично вгадати meaning довільного string.

Не всі named entities однаково доречні

HTML визначає багато named references, але source із десятками entity names не обов’язково стає кращим. Для звичайних літер UTF-8 є читабельнішим, полегшує review і зменшує кількість перетворень. References особливо корисні для syntax-significant characters та символів, намір яких складно побачити.

Команда має обрати послідовну convention і перевіряти output реальним parser. Мета полягає не в максимальній кількості entities, а в однозначному, безпечному та читабельному документі.

HTML validator може знайти malformed references, але остаточну поведінку визначає browser. Критичні шаблони варто перевіряти і статичним аналізом, і rendering tests.