flawopen.com/Vulnerabilities/Server-Side Template Injection (SSTI)
Server-Side Template Injection (SSTI): причина и исправление
Представьте компанию, печатающую письма по шаблону «Здравствуйте, {{ name }}». Безопасный процесс передаёт принтеру неизменный шаблон и имя клиента как данные. Небезопасный процесс вставляет текст клиента в инструкции шаблона до обработки. Если движок интерпретирует этот текст как синтаксис шаблона, возникает Server-Side Template Injection. Последствия зависят от движка, конфигурации, доступных объектов и привилегий процесса; вычисление выражения само по себе не означает выполнение команд операционной системы.
Основные понятия и термины подсистемы
- Шаблонизатор
- Серверный компонент, например Jinja или Twig, который разбирает синтаксис шаблона и формирует результат из шаблона и данных контекста.
- Инъекция в исходный код шаблона
- Ошибка, при которой внешний текст включается в исходный код до разбора и интерпретируется как синтаксис шаблона, а не как данные.
- Данные контекста
- Значения, передаваемые отдельно доверенному фиксированному шаблону. Такое разделение не позволяет повторно разобрать значение вроде
{{ 7 * 7 }}как исходный код. - CWE-1336
- Неправильная нейтрализация специальных элементов, используемых в шаблонизаторе. Категория описывает небезопасную обработку внешних данных как синтаксиса шаблона.
Пошаговый сценарий атаки
Ввод под контролем атакующего достигает шаблонного обработчика
Поле формы, сохранённое значение профиля или другая внешняя строка достигает кода, который создаёт или компилирует шаблон. Уязвимость связана с формированием исходного кода, а не с обычным выводом значения.
Приложение смешивает данные с инструкциями шаблона
Приложение вставляет недоверенную строку в исходный код (например, f-string передаётся в render_template_string) вместо использования статического шаблона и отдельного значения контекста.
Движок разбирает объединённый исходный код
Движок интерпретирует выражения согласно своему языку и настройкам. Безопасное арифметическое выражение может показать факт вычисления, но само по себе не доказывает доступ к файлам, секретам или системным командам.
Последствия зависят от движка и среды
Дальнейшее воздействие зависит от доступных объектов и функций, поведения песочницы, ограничений движка и привилегий веб-процесса. В некоторых конфигурациях возможна RCE, но это не автоматическое последствие каждой SSTI.
Механизм и первопричина
SSTI возникает, когда приложение включает внешний текст в программу шаблона до её разбора. В Flask/Jinja уязвимый шаблон создаёт новую строку исходного кода из данных запроса и передаёт её в render_template_string(); HTML-экранирование защищает контекст вывода, но не превращает недоверенный исходный код шаблона в данные. Исправление — загрузить фиксированный шаблон и передать значение отдельно в контексте. Если пользователям необходимо создавать шаблоны, предоставляйте минимальную модель данных и выполняйте рендеринг в изолированном процессе с ресурсными ограничениями. Песочница — дополнительный уровень защиты, а не повод передавать секреты или мощные объекты приложения.
# VULNERABLE: request data becomes template source before parsing.
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route("/greet")
def greet():
name = request.args.get("name", "Guest")
# A value such as {{ 7 * 7 }} is interpreted by the template engine.
source = f"<h1>Welcome, {name}!</h1>"
return render_template_string(source)
# HARDENED: fixed template source; request data stays a context value.
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route("/greet")
def greet():
name = request.args.get("name", "Guest")
return render_template("greet.html", name=name)
# templates/greet.html is trusted, static source:
# <h1>Welcome, {{ name }}!</h1>
Последовательность атаки и концептуальный пример
- 1Источник: Внешне контролируемое значение достигает кода, формирующего шаблон.
- 2Небезопасное объединение: Приложение вставляет значение в исходный код, а не передаёт его отдельно как данные контекста.
- 3Интерпретация: Движок разбирает объединённый исходный код. В разрешённой тестовой среде безопасное арифметическое выражение может подтвердить вычисление.
- 4Условные последствия: Дальнейший доступ зависит от движка, доступных объектов, конфигурации и привилегий; не делайте вывод о RCE только по факту вычисления выражения.
Инженерные правила защиты и предотвращения
- ✓Оставляйте исходный код неизменным: Загружайте доверенные шаблоны из файлов или контролируемого loader; передавайте пользовательский ввод только как данные контекста. Не добавляйте данные запроса в исходный код.
- ✓Различайте SSTI и HTML-инъекцию: HTML-экранирование защищает контексты вывода, но не исправляет поток, где недоверенный ввод разбирается как исходный код шаблона.
- ✓Ограничивайте пользовательские шаблоны: Если функция необходима, используйте песочницу движка по назначению, предоставляйте минимальную модель данных и выполняйте рендеринг в изолированном процессе с ограничением ресурсов. Следуйте рекомендациям движка.
- ✓Снижайте возможный ущерб: Запускайте рендерер без root-привилегий и с минимальным доступом к файлам, сети и секретам. Это локализация последствий, а не основное исправление.
- ✓Добавьте регрессионные тесты: Передайте безопасную метку вроде
{{ 7 * 7 }}как данные и убедитесь, что она остаётся данными; проверьте, что шаблоны поступают только из доверенного loader.