FlawOpen

flawopen.com/SSTI/FreeMarker y Velocity

¿Son seguras las plantillas FreeMarker y Velocity con entradas de usuario?

CWE-1336: Server-Side Template InjectionPágina de referencia
Respuesta breve

Los valores del usuario siguen siendo datos cuando se insertan en una plantilla confiable. El código fuente de plantilla controlado por el usuario es distinto: permite escribir expresiones en el lenguaje del motor. El alcance depende de la versión, el wrapper o introspector, los objetos del modelo y los permisos de la aplicación. No supongas que toda inyección de plantillas ejecuta comandos del sistema; trata las plantillas no confiables como lógica ejecutable y mantenlas fuera del proceso de la aplicación.

VULNERABLE
// Riesgo: analizar datos de la solicitud como plantilla
String src = "Hello " + userSuppliedName;
Template t = new Template("t", src, cfg);
t.process(model, writer);

// Riesgo: evaluar una cadena VTL controlada por el usuario
Velocity.evaluate(context, writer,
                  "log", userTemplate);
CORREGIDO
// Plantilla confiable del classpath; modelo solo con escalares
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: plantilla confiable e introspección restringida (defensa en profundidad)
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);

Por qué los motores Java pueden exponer capacidades peligrosas

Las plantillas evalúan expresiones sobre un modelo de datos, pero la superficie Java disponible depende de la configuración. El wrapper predeterminado de FreeMarker puede exponer propiedades y métodos JavaBean de POJO arbitrarios; los miembros accesibles dependen del wrapper y de la política de acceso. El Uberspect predeterminado de Velocity relaciona propiedades VTL con métodos Java públicos. Esto no significa que cualquier modelo permita reflexión sin restricciones: el riesgo aumenta si contiene objetos de servicio, cargadores de clases, objetos de solicitud u otras capacidades potentes. Prefiere mapas de cadenas y valores simples; expón solo los campos que necesite cada plantilla confiable.

El built-in ?new de FreeMarker puede instanciar clases que implementan TemplateModel. Apache recomienda restringir la resolución de clases para plantillas cargadas por usuarios; SAFER_RESOLVER aún permite algunas clases, mientras que ALLOWS_NOTHING_RESOLVER deniega toda resolución. Esto no sustituye un wrapper restringido ni un modelo mínimo.

Cómo reducir el riesgo y conocer los límites

Trata las restricciones del motor como defensa en profundidad, no como aislamiento de procesos. Usa versiones compatibles, revisa la configuración exacta según la guía vigente y mantén el renderizador lejos de secretos y objetos privilegiados.

Preguntas frecuentes

¿También afecta a Thymeleaf?

Es un riesgo relacionado, pero distinto. Un nombre de vista controlado por el usuario no constituye automáticamente una inyección de expresiones. Revisa el flujo de datos concreto hacia la evaluación de Thymeleaf o Spring Expression Language y no construyas fragmentos de expresión con entradas no confiables. Mantén las integraciones actualizadas y selecciona vistas mediante lógica confiable de la aplicación.

Solo los administradores pueden editar plantillas. ¿Es aceptable?

Quienes escriben plantillas pueden usar las capacidades expuestas por el motor y el modelo; eso no siempre significa ejecutar comandos del sistema operativo. Trata el permiso de edición como un privilegio sensible de autoría de código, limítalo a mantenedores de confianza y registra los cambios.

¿Qué motor es más seguro para plantillas aportadas por usuarios?

Un motor sin lógica como Mustache puede reducir la superficie de expresiones y llamadas a métodos, pero siguen importando el escape de salida, el control del origen de la plantilla y las capacidades de los helpers. Elige un motor diseñado para ese modelo de confianza y aísla el renderizado; no uses un motor JVM general como sandbox.

Fuentes oficiales y referencia

Véase también SSTI: causa, impacto y corrección de template injection ¿Es seguro render_template_string() de Jinja2? ¿Qué es la inyección de plantillas del lado del servidor?