Escaping HTML

В i18next интерполяция значений выполняется через синтаксис {{ key }} внутри переводов. При подстановке значений библиотека по умолчанию применяет экранирование специальных HTML-символов, чтобы предотвращать внедрение разметки в итоговую строку.

При интерполяции происходит преобразование потенциально опасных символов:

  • <&lt;
  • >&gt;
  • &&amp;
  • "&quot;
  • '&#039;

Это поведение связано с защитой от XSS-уязвимостей в тех случаях, когда перевод или интерполируемые данные попадают напрямую в DOM.

Параметр escapeValue и его роль

Ключевой механизм управления экранированием задаётся через конфигурацию интерполяции:

i18next.init({
  interpolation: {
    escapeValue: true
  }
});

Поведение при escapeValue: true

При включённом экранировании все значения, вставляемые через {{ }}, проходят через функцию escape. Это безопасное поведение для классического DOM-рендеринга:

{
  "welcome": "Hello, {{name}}"
}
i18next.t('welcome', { name: '<script>alert(1)</script>' });

Результат:

Hello, &lt;script&gt;alert(1)&lt;/script&gt;

HTML-код не интерпретируется браузером как разметка.

Поведение при escapeValue: false

Отключение экранирования приводит к прямой подстановке значений без преобразования:

i18next.init({
  interpolation: {
    escapeValue: false
  }
});

В этом режиме:

Hello, <script>alert(1)</script>

Данный режим допустим только в средах, где экранирование выполняется на другом уровне (например, React).

Неэкранированная интерполяция и вставка HTML

i18next поддерживает механизм вставки «сырых» HTML-значений через тройные фигурные скобки:

{{{html}}}

Пример:

{
  "content": "Text: {{{value}}}"
}
i18next.t('content', {
  value: '<strong>bold</strong>'
});

Результат:

Text: <strong>bold</strong>

HTML интерпретируется браузером как разметка и не экранируется.

Использование такого подхода полностью обходит защитный слой интерполяции и переносит ответственность за безопасность на уровень данных.

Различие между {{ }} и {{{ }}}

Синтаксис Поведение Экранирование Результат
{{ key }} интерполяция включено по умолчанию текст
{{{ key }}} сырая вставка отключено HTML

Двойные скобки предназначены для безопасной подстановки значений, тройные — для управляемого внедрения HTML.

Влияние React и других UI-библиотек

В средах, использующих виртуальный DOM (например, React), дополнительное экранирование часто уже встроено на уровне рендера.

Поэтому конфигурация i18next обычно включает:

i18next.init({
  interpolation: {
    escapeValue: false
  }
});

Причина заключается в том, что React самостоятельно преобразует строки в безопасное отображение, предотвращая прямое выполнение HTML.

Однако даже в таких условиях использование {{{ }}} может приводить к небезопасной вставке через dangerouslySetInnerHTML, если данные не контролируются.

Потенциальные уязвимости при отключённом экранировании

Отключение escapeValue или использование тройных скобок создаёт риск внедрения скриптов, если входные данные не фильтруются.

Типичный сценарий:

i18next.t('message', {
  name: userInput
});

Если userInput содержит HTML или JavaScript, он может быть внедрён в DOM при отсутствии экранирования.

Особенно опасны комбинации:

  • пользовательский ввод + {{{ }}}
  • API-данные без санитизации + отключённое escapeValue
  • вставка HTML в атрибуты DOM

Сценарии, в которых экранирование критично

Экранирование становится обязательным при:

  • прямом выводе текста в HTML через innerHTML
  • серверной генерации HTML-страниц
  • использовании переводов в email-шаблонах
  • рендеринге пользовательских данных без дополнительной обработки

Управление безопасной вставкой HTML

При необходимости работы с HTML содержимым применяется явная санитизация данных до передачи в i18next.

Пример концептуальной обработки:

function sanitize(input) {
  return input.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

Затем уже безопасное значение передаётся в интерполяцию:

i18next.t('text', {
  value: sanitize(userInput)
});

Постобработка и влияние на экранирование

i18next поддерживает post-processing плагинизацию, где итоговая строка может дополнительно модифицироваться после интерполяции.

На этом этапе возможно:

  • повторное экранирование
  • декодирование HTML сущностей
  • трансформация текста в DOM-структуру

Однако любые операции на этом уровне должны учитывать, что интерполяция уже могла изменить исходные данные.

Особенности вложенной интерполяции

i18next допускает вложенные значения:

{
  "outer": "Value: {{inner}}"
}
i18next.t('outer', {
  inner: i18next.t('innerKey')
});

Если внутренний перевод содержит HTML или небезопасные данные, экранирование применяется только на уровне финальной строки, что может привести к неожиданным результатам при комбинировании режимов.

Поведение с объектами и массивами

При включённой опции returnObjects интерполяция может работать с вложенными структурами:

i18next.t('key', { returnObjects: true });

Если такие данные используются в UI без корректного рендера, экранирование может оказаться недостаточным, поскольку объекты могут быть преобразованы в строки через toString().

Контроль уровня доверия к данным

Вся модель экранирования в i18next опирается на разделение данных по источнику:

  • переводы считаются доверенными
  • пользовательский ввод считается недоверенным
  • API-ответы находятся в промежуточной зоне

При смешивании этих источников в одной интерполяции риск XSS возрастает линейно с уменьшением контроля над данными.

Итоговая модель обработки строки

Процесс интерполяции можно представить как последовательность этапов:

  1. загрузка перевода
  2. извлечение плейсхолдеров {{ }} или {{{ }}}
  3. подстановка значений
  4. экранирование (если включено)
  5. постобработка
  6. передача в рендер

Любое отключение экранирования или использование «сырых» вставок изменяет конечный результат уже на этапе 4, что делает последующие этапы критичными для безопасности DOM-вывода.