FlawOpen

flawopen.com/Vulnerabilities/Server-Side Template Injection (SSTI)

Server-Side Template Injection (SSTI): причина и исправление

Impact depends on reachable template behavior CWE-1336 Template Engine Security
💡 Простое объяснение — данные или инструкции принтеру

Представьте компанию, печатающую письма по шаблону «Здравствуйте, {{ name }}». Безопасный процесс передаёт принтеру неизменный шаблон и имя клиента как данные. Небезопасный процесс вставляет текст клиента в инструкции шаблона до обработки. Если движок интерпретирует этот текст как синтаксис шаблона, возникает Server-Side Template Injection. Последствия зависят от движка, конфигурации, доступных объектов и привилегий процесса; вычисление выражения само по себе не означает выполнение команд операционной системы.

Цель: формирование исходного кода и серверный рендеринг шаблонов в Jinja, Twig, FreeMarker, ERB и подобных движках.
Уязвимая граница: недоверенный ввод компилируется или разбирается как исходный код шаблона вместо передачи значения доверенному шаблону.
Воздействие: вычисление выражений; дальнейшие последствия зависят от возможностей движка, доступных объектов, песочницы и привилегий процесса.
Исправление: сохраняйте доверенный исходный код шаблона неизменным; передавайте недоверенный ввод только как данные контекста. Пользовательские шаблоны при необходимости обрабатывайте изолированно.

Основные понятия и термины подсистемы

Шаблонизатор
Серверный компонент, например Jinja или Twig, который разбирает синтаксис шаблона и формирует результат из шаблона и данных контекста.
Инъекция в исходный код шаблона
Ошибка, при которой внешний текст включается в исходный код до разбора и интерпретируется как синтаксис шаблона, а не как данные.
Данные контекста
Значения, передаваемые отдельно доверенному фиксированному шаблону. Такое разделение не позволяет повторно разобрать значение вроде {{ 7 * 7 }} как исходный код.
CWE-1336
Неправильная нейтрализация специальных элементов, используемых в шаблонизаторе. Категория описывает небезопасную обработку внешних данных как синтаксиса шаблона.

Пошаговый сценарий атаки

Step 1

Ввод под контролем атакующего достигает шаблонного обработчика

Поле формы, сохранённое значение профиля или другая внешняя строка достигает кода, который создаёт или компилирует шаблон. Уязвимость связана с формированием исходного кода, а не с обычным выводом значения.

Step 2

Приложение смешивает данные с инструкциями шаблона

Приложение вставляет недоверенную строку в исходный код (например, f-string передаётся в render_template_string) вместо использования статического шаблона и отдельного значения контекста.

Step 3

Движок разбирает объединённый исходный код

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

Step 4

Последствия зависят от движка и среды

Дальнейшее воздействие зависит от доступных объектов и функций, поведения песочницы, ограничений движка и привилегий веб-процесса. В некоторых конфигурациях возможна RCE, но это не автоматическое последствие каждой SSTI.

Механизм и первопричина

SSTI возникает, когда приложение включает внешний текст в программу шаблона до её разбора. В Flask/Jinja уязвимый шаблон создаёт новую строку исходного кода из данных запроса и передаёт её в render_template_string(); HTML-экранирование защищает контекст вывода, но не превращает недоверенный исходный код шаблона в данные. Исправление — загрузить фиксированный шаблон и передать значение отдельно в контексте. Если пользователям необходимо создавать шаблоны, предоставляйте минимальную модель данных и выполняйте рендеринг в изолированном процессе с ресурсными ограничениями. Песочница — дополнительный уровень защиты, а не повод передавать секреты или мощные объекты приложения.

flask_app.py (Уязвимый шаблон)Уязвимый
# 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)
flask_app.py (Защищённый)Защищённый
# 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>

Последовательность атаки и концептуальный пример

Инженерные правила защиты и предотвращения

Основные источники и смежные темы безопасности:Полный каталог безопасности →
CWE-1336: инъекция в шаблонизатор → OWASP WSTG: Server-Side Template Injection → Jinja: песочница и безопасность → API Jinja: шаблоны и loaders → Flask: рендеринг шаблонов →