FlawOpen

flawopen.com/SSTI/FreeMarker и Velocity

Безопасны ли шаблоны FreeMarker и Velocity при пользовательском вводе?

CWE-1336: Server-Side Template InjectionСправочная страница
Краткий ответ

Пользовательские значения остаются данными, если подставляются в доверенный шаблон. Пользовательский исходный текст шаблона — другое: он позволяет задавать выражения на языке шаблонизатора. Доступные возможности зависят от версии, wrapper или Uberspector, объектов модели и прав приложения. Не следует считать, что любая инъекция шаблона выполняет команды ОС; недоверенный текст шаблона нужно считать исполняемой логикой и рендерить вне процесса приложения.

УЯЗВИМО
// Риск: разбор данных запроса как исходника шаблона
String src = "Hello " + userSuppliedName;
Template t = new Template("t", src, cfg);
t.process(model, writer);

// Риск: вычисление строки VTL под контролем пользователя
Velocity.evaluate(context, writer,
                  "log", userTemplate);
ИСПРАВЛЕНО
// Доверенный шаблон classpath; в модели только скаляры
Configuration cfg = new Configuration(
    Configuration.VERSION_2_3_34);
cfg.setTemplateLoader(new ClassTemplateLoader(
    App.class, "/templates"));
cfg.setNewBuiltinClassResolver(
    TemplateClassResolver.ALLOWS_NOTHING_RESOLVER);
cfg.setObjectWrapper(new SimpleObjectWrapper(
    Configuration.VERSION_2_3_34));
Template t = cfg.getTemplate("hello.ftl");
Map<String,Object> model = Map.of(
    "name", userSuppliedName);
t.process(model, writer);

// Velocity: доверенный шаблон и ограниченная интроспекция (эшелонированная защита)
Properties p = new Properties();
p.setProperty("resource.loaders", "class");
p.setProperty("resource.loader.class.class",
    ClasspathResourceLoader.class.getName());
p.setProperty("runtime.introspector.uberspect",
    SecureUberspector.class.getName());
VelocityEngine engine = new VelocityEngine();
engine.init(p);
Template vt = engine.getTemplate("hello.vm", "UTF-8");
VelocityContext vc = new VelocityContext();
vc.put("name", userSuppliedName);
vt.merge(vc, writer);

Почему шаблонизаторы Java могут открыть опасные возможности

Шаблоны вычисляют выражения относительно модели данных, но доступная поверхность Java зависит от конфигурации. Стандартный wrapper FreeMarker может открыть JavaBean-свойства и методы произвольных POJO; доступные члены зависят от wrapper и политики доступа. Стандартный Uberspect Velocity сопоставляет свойства VTL с публичными методами Java. Это не означает, что любая модель дает неограниченный доступ к reflection: риск выше, если в модели есть сервисные объекты, загрузчики классов, объекты запросов или другие мощные возможности. Предпочитайте карты строк и простые значения; передавайте шаблону только необходимые поля.

Встроенная функция FreeMarker ?new может создавать классы, реализующие TemplateModel. Apache рекомендует ограничивать разрешение классов для загружаемых шаблонов; SAFER_RESOLVER по-прежнему разрешает некоторые классы, а ALLOWS_NOTHING_RESOLVER запрещает разрешение всех классов. Это не заменяет ограниченный wrapper и минимальную модель данных.

Как снизить риск и учитывать ограничения

Считайте ограничения движка эшелонированной защитой, а не изоляцией процесса. Используйте поддерживаемые версии, сверяйте конфигурацию с актуальными рекомендациями и не предоставляйте рендереру секреты или привилегированные объекты.

Частые вопросы

Это также касается Thymeleaf?

Это связанный, но отдельный риск. Имя представления под контролем пользователя само по себе не является уязвимостью внедрения выражения. Проверьте конкретный поток данных к вычислению Thymeleaf или Spring Expression Language и не создавайте фрагменты выражений из недоверенного ввода. Обновляйте интеграции и выбирайте представления доверенной логикой приложения.

Шаблоны могут менять только администраторы. Это приемлемо?

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

Какой движок безопаснее для шаблонов пользователей?

Шаблонизатор без логики, например Mustache, может сократить поверхность выражений и вызовов методов, но экранирование вывода, контроль источника шаблона и возможности helper-функций все равно важны. Выберите движок под эту модель доверия и изолируйте рендеринг; не полагайтесь на универсальный движок JVM как на песочницу.

Официальные источники и справка

См. также SSTI: причина, последствия и защита от инъекции в шаблон Безопасна ли функция Jinja2 render_template_string()? Что такое серверная инъекция в шаблон?