i18next предоставляет систему интерполяции строк, которая позволяет подставлять значения в переводы. Любая интерполяция потенциально связана с рисками внедрения нежелательного HTML или JavaScript-кода, поэтому механизм экранирования является ключевым элементом безопасности.
Основная задача escaping в i18next — предотвращение XSS-уязвимостей при подстановке динамических данных в переводимые строки, особенно в веб-приложениях, где результат рендерится в DOM.
В i18next интерполяция используется через синтаксис:
i18next.t('welcome', { name: 'John' })
Перевод:
{
"welcome": "Hello, {{name}}"
}
Результат:
Hello, John
Если значение содержит HTML:
i18next.t('welcome', { name: '<b>John</b>' })
поведение зависит от настройки escapeValue.
Ключевая настройка интерполяции:
interpolation: {
escapeValue: true
}
escapeValue: true — значения экранируютсяescapeValue: false — значения вставляются как естьПример конфигурации:
i18next.init({
interpolation: {
escapeValue: true
}
});
При включённом escaping специальные символы преобразуются:
| Символ | Замена |
|---|---|
< |
< |
> |
> |
& |
& |
" |
" |
' |
' |
Это предотвращает внедрение HTML и JavaScript через строки перевода.
Отключение:
interpolation: {
escapeValue: false
}
создаёт риск XSS, если данные поступают извне:
i18next.t('welcome', { name: userInput })
Если userInput содержит:
<script>alert(1)</script>
и escaping отключён, код будет вставлен в DOM без изменений.
В браузерных приложениях escaping критически важен, если результат:
innerHTMLПри SSR (Node.js):
В связке с React поведение меняется:
Типичная конфигурация:
i18next.init({
interpolation: {
escapeValue: false
}
});
Причина:
{value}Пример:
<div>{t('welcome', { name })}</div>
React превращает <b> в текст, а не HTML.
Несмотря на автоматическое экранирование React, escaping в i18next может быть полезен:
dangerouslySetInnerHTMLПример опасного использования:
<div dangerouslySetInnerHTML={{ __html: t('welcome', { name }) }} />
В этом случае escaping i18next становится защитным барьером.
i18next поддерживает разные подходы к вставке значений.
{
"text": "User: {{name}}"
}
Результат:
User: <b>John</b>
{
"text": "User: {{- name}}"
}
Синтаксис {{- value}} отключает escaping для конкретного
параметра.
Результат:
User: <b>John</b>
Можно комбинировать безопасные и небезопасные вставки:
{
"text": "Hello {{name}}, role: {{- roleHtml}}"
}
Использование:
i18next.t('text', {
name: '<John>',
roleHtml: '<b>admin</b>'
});
Результат:
Hello <John>, role: <b>admin</b>
i18next позволяет переопределить механизм экранирования:
i18next.init({
interpolation: {
escape: function (str) {
return str.replace(/</g, '<').replace(/>/g, '>');
}
}
});
Это используется когда:
Escaping применяется после интерполяции вложенных значений:
{
"nested": "Value: {{data.value}}"
}
i18next.t('nested', {
data: {
value: '<script>'
}
});
Результат при escapeValue: true:
Value: <script>
Процесс интерполяции и escaping:
Важно, что escaping выполняется после интерполяции, а не до неё.
При использовании форматов:
interpolation: {
format: (value, format) => {
if (format === 'uppercase') return value.toUpperCase();
return value;
}
}
escaping применяется уже к результату форматирования:
format(value) → escape(result)
Escaping не изменяет:
Пример:
i18next.t('hello', { name: 'Иван ?' })
Результат остаётся читаемым, без преобразования Unicode.
escapeValue: true (i18next)
+ React escaping
Результат:
&lt;b&gt;
escapeValue: false
при использовании пользовательского ввода приводит к XSS.
{{- value}}
Без фильтрации данных это равнозначно вставке HTML.
escapeValue: true вне React{{- value}} для внешних данныхEscaping в i18next — не только защитный механизм, но и часть архитектуры интернационализации:
В сложных системах интернационализации escaping становится границей между доверенными и недоверенными данными, определяя уровень безопасности всей текстовой подсистемы приложения.