HTML-кодирование сущностей и URL-кодирование: ключевые различия
HTML-кодирование сущностей преобразует символы вроде <, >, & и " в HTML-сущности (<, >, &, ") для безопасного отображения на веб-страницах. URL-кодирование преобразует символы в формат %XX для безопасной передачи в URL. Они защищают от разных типов атак и применяются в разных контекстах.
Что такое HTML-кодирование сущностей?
HTML-кодирование сущностей (также называемое HTML-экранированием) преобразует символы, имеющие специальное значение в HTML, в ссылки на сущности, которые браузер отображает как буквальный символ, а не интерпретирует как разметку. Наиболее важные символы для кодирования — это <, >, &, " и '.
Без HTML-кодирования строка вроде <script>alert('XSS')</script> была бы выполнена браузером как JavaScript. После кодирования она превращается в <script>alert('XSS')</script> и отображается как безобидный текст.
HTML-сущности можно записывать в двух формах: именованные сущности, такие как &, < и ", или числовые сущности, такие как &, < и ". Числовые сущности используют кодовую точку Unicode символа и могут представлять любой символ Unicode.
<!-- До HTML-кодирования -->
<p>The formula is: x < y & y > z</p>
<!-- Браузер интерпретирует < как начало тега! -->
<!-- После HTML-кодирования -->
<p>The formula is: x < y & y > z</p>
<!-- Браузер отображает: x < y & y > z -->
<!-- Распространённые HTML-сущности -->
& → & (амперсанд)
< → < (меньше чем)
> → > (больше чем)
" → " (двойная кавычка)
' → ' (одинарная кавычка / апостроф)
→ (неразрывный пробел)
Что такое URL-кодирование?
URL-кодирование (percent-encoding) преобразует символы, небезопасные в URL, в знак процента, за которым следуют две шестнадцатеричные цифры, представляющие значение байта. Оно определено стандартом RFC 3986 и гарантирует, что URL содержат только допустимые ASCII-символы, которые можно безопасно передавать по интернету.
URL-кодирование необходимо, потому что URL имеют строгий синтаксис, в котором определённые символы служат разделителями (/, ?, &, #, =). Когда эти символы встречаются в данных (например, в значениях параметров запроса), их нужно закодировать, чтобы они не были интерпретированы как структурные элементы.
// Примеры URL-кодирования
space → %20
& → %26
= → %3D
? → %3F
# → %23
/ → %2F
+ → %2B
// Полный URL с закодированным параметром запроса
https://example.com/search?q=salt%20%26%20pepper
// Значение q равно "salt & pepper"
Сравнительная таблица
| Характеристика | HTML-кодирование сущностей | URL-кодирование |
|---|---|---|
| Назначение | Безопасное отображение на HTML-страницах | Безопасная передача в URL |
| Стандарт | Спецификация HTML/WHATWG | RFC 3986 |
| Формат | &name; или &#number; |
%XX (шестнадцатеричное значение байта) |
| Пробел становится | (неразрывный) или остаётся как есть |
%20 или + |
| & становится | & |
%26 |
| < становится | < |
%3C |
| Предотвращает | XSS-атаки, сломанный HTML | Сломанные URL, атаки внедрением |
| Контекст | Тело и атрибуты HTML-документа | Пути URL, строки запроса, фрагменты |
| Функция JS | Нет встроенной (используйте библиотеку или DOM API) | encodeURIComponent() |
Когда нужны оба?
Существуют распространённые ситуации, когда требуется и HTML-кодирование, и URL-кодирование. Самый частый случай — когда вы помещаете URL внутрь HTML-атрибута, такого как href или src. Сначала URL нужно правильно закодировать в формате URL, а затем любые специальные HTML-символы в результате должны быть HTML-закодированы.
<!-- Шаг 1: URL-кодирование значения параметра запроса -->
<!-- Значение: "Tom & Jerry" → в URL-кодировке: "Tom%20%26%20Jerry" -->
<!-- Шаг 2: Сборка URL -->
<!-- URL: https://example.com/search?q=Tom%20%26%20Jerry -->
<!-- Шаг 3: HTML-кодирование URL для использования в атрибуте href -->
<a href="https://example.com/search?q=Tom%20%26%20Jerry">
Search for Tom & Jerry
</a>
<!-- В этом случае последовательности % не нужно HTML-кодировать, -->
<!-- потому что % не является специальным HTML-символом -->
<!-- Но если URL содержит & как разделитель, его ОБЯЗАТЕЛЬНО нужно HTML-кодировать: -->
<a href="https://example.com/search?q=cats&sort=name">
Search cats sorted by name
</a>
<!-- Без & браузер может интерпретировать &sort как HTML-сущность -->
В современных фреймворках, таких как React, Vue и Angular, HTML-кодирование выполняется автоматически при использовании шаблонных выражений или JSX. Однако URL-кодирование по-прежнему нужно выполнять явно при построении URL из динамических данных.
// React — HTML-кодирование автоматическое, URL-кодирование — нет
function SearchLink({ query }) {
// URL-кодирование должно быть явным
const url = '/search?q=' + encodeURIComponent(query);
// HTML-кодирование в JSX выполняется автоматически
return <a href={url}>Search for {query}</a>;
// React автоматически HTML-кодирует и href, и текстовое содержимое
}
Предотвращение XSS: какое кодирование где использовать
Предотвращение межсайтового скриптинга (XSS) требует применения правильного кодирования в зависимости от контекста, куда помещается пользовательский ввод. Использование неверного типа кодирования в конкретном контексте не обеспечивает никакой защиты.
- Контекст тела HTML: используйте HTML-кодирование сущностей. Это предотвращает интерпретацию внедрённых тегов вроде
<script>как разметки. - Контекст атрибута HTML: используйте HTML-кодирование сущностей и всегда заключайте значения атрибутов в кавычки. Из атрибутов без кавычек можно вырваться даже при HTML-кодировании.
- Контекст URL (href, src): используйте URL-кодирование для динамических частей, затем HTML-кодируйте весь URL, если помещаете его в HTML. Проверяйте, что URL начинается с безопасной схемы (
https:), чтобы предотвратитьjavascript:-URL. - Контекст JavaScript: используйте экранирование строк JavaScript (JSON.stringify или специализированную библиотеку). Ни HTML-кодирование, ни URL-кодирование недостаточны в контексте JavaScript.
- Контекст CSS: используйте CSS-экранирование. HTML- и URL-кодирование не защищают от CSS-инъекций.
Ключевой принцип таков: кодируйте под контекст вывода, а не под источник ввода. Один и тот же фрагмент пользовательского ввода может требовать разного кодирования в зависимости от того, где он появляется в ответе. Никогда не полагайтесь только на валидацию ввода для предотвращения XSS; всегда применяйте кодирование вывода.