flawopen.com/SSTI/FreeMarker и Velocity
Безопасны ли шаблоны FreeMarker и Velocity при пользовательском вводе?
Пользовательские значения остаются данными, если подставляются в доверенный шаблон. Пользовательский исходный текст шаблона — другое: он позволяет задавать выражения на языке шаблонизатора. Доступные возможности зависят от версии, 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 и минимальную модель данных.
Как снизить риск и учитывать ограничения
- ✓FreeMarker: для недоверенных шаблонов задайте
new_builtin_class_resolverкакALLOWS_NOTHING_RESOLVERи используйте wrapper вродеSimpleObjectWrapper, который отклоняет произвольные POJO. Передавайте только простые значения для конкретной задачи. - ✓Velocity: используйте
SecureUberspectorдля недоверенных шаблонов и оставляйте в контексте только необходимые простые значения. Он блокирует документированные пути reflection и создания объектов, но не является песочницей ОС. - ✓Оба движка: загружайте шаблоны приложения из доверенного источника с защитой от изменений. Не превращайте параметры запроса или изменяемые пользователем строки БД в исходный текст шаблона.
- ✓Если пользовательские шаблоны необходимы продукту: выполняйте рендеринг в отдельном процессе с минимальными правами, без сети, секретов и доступных для записи файлов приложения; ограничивайте CPU, память и время извне движка.
Считайте ограничения движка эшелонированной защитой, а не изоляцией процесса. Используйте поддерживаемые версии, сверяйте конфигурацию с актуальными рекомендациями и не предоставляйте рендереру секреты или привилегированные объекты.
Частые вопросы
Это связанный, но отдельный риск. Имя представления под контролем пользователя само по себе не является уязвимостью внедрения выражения. Проверьте конкретный поток данных к вычислению Thymeleaf или Spring Expression Language и не создавайте фрагменты выражений из недоверенного ввода. Обновляйте интеграции и выбирайте представления доверенной логикой приложения.
Авторы шаблонов могут использовать возможности движка и модели данных, но это не всегда означает выполнение команд ОС. Считайте право редактирования чувствительной привилегией создания кода, ограничьте ее доверенными сопровождающими и регистрируйте изменения.
Шаблонизатор без логики, например Mustache, может сократить поверхность выражений и вызовов методов, но экранирование вывода, контроль источника шаблона и возможности helper-функций все равно важны. Выберите движок под эту модель доверия и изолируйте рендеринг; не полагайтесь на универсальный движок JVM как на песочницу.
Официальные источники и справка
- Apache FreeMarker — FAQ о безопасности загружаемых шаблонов
- Apache FreeMarker — API SimpleObjectWrapper
- Apache FreeMarker — API TemplateClassResolver
- Apache Velocity — руководство разработчика
- Apache Velocity 2.3 — API ClasspathResourceLoader
- PortSwigger — Server-Side Template Injection
- CWE-1336 — Server-Side Template Injection