Безопасная интерполяция

Безопасная интерполяция в i18next является ключевым механизмом защиты от внедрения вредоносного кода при подстановке динамических значений в переводы. Интерполяция в i18next используется повсеместно: подстановка имён пользователей, числовых значений, URL, фрагментов HTML и структурированных данных. Ошибки в её конфигурации приводят к XSS-уязвимостям, особенно в приложениях без дополнительного слоя экранирования.

Интерполяция основана на шаблонных выражениях внутри строк перевода:

{
  "welcome": "Привет, {{name}}!"
}

При вызове:

i18next.t('welcome', { name: 'Алексей' })

результат:

Привет, Алексей!

Система заменяет {{name}} на переданное значение, используя встроенный интерполяционный процессор.

Потенциальная проблема безопасности

Основной риск возникает при подстановке значений, содержащих HTML или Jav * aScript:

i18next.t('welcome', { name: '<img src=x oner ror=alert(1) />' })

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

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

  • innerHTML
  • server-side HTML hydration
  • шаблонов без виртуального DOM
  • устаревших конфигураций i18next

Экранирование как механизм защиты

В i18next интерполяция по умолчанию включает экранирование:

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

Принцип работы escapeValue

При включённом escapeValue символы:

  • <
  • >
  • &
  • "
  • '

заменяются на HTML-сущности:

< → &lt;
> → &gt;
& → &amp;

Таким образом:

i18next.t('welcome', { name: '<b>test</b>' })

становится:

Привет, &lt;b&gt;test&lt;/b&gt;

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

Отключение экранирования и его последствия

В некоторых конфигурациях встречается:

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

Это допустимо только при условии, что слой рендеринга уже обеспечивает защиту (например, React).

При использовании DOM напрямую:

element.innerHTML = i18next.t('welcome', { name })

отключение экранирования создаёт прямую XSS-уязвимость.

React и автоматическая защита

В React i18next экранирование часто отключается:

interpolation: {
  escapeValue: false
}

Причина заключается в том, что React сам экранирует значения при рендеринге:

<div>{t('welcome', { name })}</div>

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

Однако опасность возникает при использовании:

<div dangerouslySetInnerHTML={{ __html: t('welcome', { name }) }} />

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

HTML-интерполяция и теги

i18next поддерживает вставку HTML-подобных структур через интерполяцию, например:

{
  "message": "Нажми <strong>{{button}}</strong>"
}

При небезопасной конфигурации это может быть интерпретировано как HTML.

Для безопасной работы применяется разделение:

  • текстовые значения — через {{}}
  • HTML — через компонентные подходы (React components, Trans components)

Использование React Trans для безопасной вставки

Более безопасный подход:

<Trans i18nKey="message">
  Нажми <strong>{{button}}</strong>
</Trans>

Здесь React контролирует структуру DOM, а i18next не выполняет прямую HTML-инъекцию.

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

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

i18next.init({
  interpolation: {
    escapeValue: true,
    escape: (value) => {
      return value.replace(/[&<>"']/g, (char) => {
        const map = {
          '&': '&amp;',
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#39;'
        };
        return map[char];
      });
    }
  }
});

Такой подход используется в средах, где требуется строгий контроль над процессом санитизации.

Форматы интерполяции и вложенные значения

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

i18next.t('user.info', {
  user: {
    name: '<script>alert(1)</script>'
  }
});

и ключ:

{
  "user": {
    "info": "Пользователь: {{user.name}}"
  }
}

Без экранирования вложенные значения также становятся источником XSS.

Обработка чисел и дат

Интерполяция может работать с форматированием:

i18next.t('price', {
  value: 1234.56,
  format: 'currency'
});

Безопасность здесь связана не только с XSS, но и с подменой данных при некорректной локализации.

Проблемы при server-side rendering

На сервере интерполяция формирует HTML до передачи клиенту:

const html = i18next.t('page', { name });

Если данные не экранированы, вредоносный код попадает в исходный HTML ответа.

Особенно критично при:

  • Next.js SSR
  • Express + template engines
  • static HTML generation

Опасные паттерны использования

Наиболее рискованные сценарии:

// небезопасно
element.innerHTML = i18next.t('key', data);

// небезопасно при escapeValue: false
i18next.t('key', { name: userInput });

// небезопасно при SSR без фильтрации
res.send(i18next.t('page', data));

Безопасные паттерны использования

Корректные подходы:

// безопасно с React
<div>{t('key', data)}</div>
// безопасно при escapeValue: true
i18next.t('key', data)
// безопасная структура через Trans
<Trans i18nKey="key">
  <strong />
</Trans>

Интерполяция и типизация значений

При работе с TypeScript важно учитывать типизацию значений:

i18next.t<{ name: string }>('welcome', {
  name: user.name
});

Это снижает риск передачи неожиданных структур, которые могут привести к некорректной сериализации.

Ограничение интерполяции

i18next позволяет ограничивать поведение интерполяции:

i18next.init({
  interpolation: {
    skipOnVariables: false,
    defaultVariables: {
      appName: 'MyApp'
    }
  }
});

Это помогает контролировать предсказуемость данных и снижает вероятность подмены значений.

Контекст и безопасная подстановка

Контекстная система:

i18next.t('button', { context: 'save' });

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

Санитизация до интерполяции

Наиболее надёжный подход — очистка данных до передачи в i18next:

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

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

Такой подход снижает нагрузку на механизм экранирования и исключает риск ошибок конфигурации.

Интерполяция объектов и сериализация

i18next может интерполировать объекты:

i18next.t('key', { user: { name: 'Alex' } });

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

[object Object]

или содержать нежелательные данные при кастомных объектах.

Контроль сериализации предотвращает утечку структур и косвенные уязвимости.

Итоговые принципы безопасной интерполяции

  • экранирование должно быть включено при работе вне защищённых UI-фреймворков
  • любые пользовательские данные рассматриваются как небезопасные
  • DOM-инъекции через innerHTML требуют строгого контроля
  • вложенные значения требуют такого же уровня защиты, как и плоские
  • server-side рендеринг усиливает последствия ошибок интерполяции
  • безопасные компоненты предпочтительнее HTML-строк
  • отключение escapeValue допустимо только при гарантированной внешней защите