flawopen.com/SSTI/FreeMarker & Velocity
Are FreeMarker and Velocity Templates Safe with User Input?
User values are data when inserted into a trusted template. User-controlled template source is different: it lets the user write expressions in the template language. What those expressions can reach depends on engine version, object wrapper or introspector, model objects, and application permissions. Do not assume every template injection can execute an operating-system command; do treat untrusted template source as executable logic and keep it out of the application process.
// Risk: parse request data as template source String src = "Hello " + userSuppliedName; Template t = new Template("t", src, cfg); t.process(model, writer); // Risk: evaluate a user-controlled VTL string Velocity.evaluate(context, writer, "log", userTemplate);
// Trusted classpath template; scalar-only model 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: trusted template with restricted introspection 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);
Why Java engines escalate so cleanly
Templates evaluate expressions against a data model, but the Java surface exposed to those expressions is configuration-dependent. FreeMarker's default object wrapper can expose JavaBean properties and methods for arbitrary POJOs; the available members depend on the wrapper and member-access policy. Velocity's default Uberspect maps template properties to public Java methods. Neither fact means every model exposes unrestricted reflection: risk increases when the model contains service objects, class loaders, request objects, or other powerful capabilities. Prefer maps of strings and other simple values, and expose only the fields each trusted template needs.
FreeMarker's ?new built-in can instantiate classes that implement TemplateModel. Apache advises restricting class resolution for uploaded templates; its SAFER_RESOLVER still permits some classes, while ALLOWS_NOTHING_RESOLVER denies all class resolution. This control does not replace a restricted object wrapper and a minimal data model.
Hardening, and its limits
- ✓FreeMarker: for untrusted templates, set
new_builtin_class_resolvertoALLOWS_NOTHING_RESOLVERand use a wrapper such asSimpleObjectWrapperthat rejects arbitrary POJOs. Pass only simple, purpose-built model values. - ✓Velocity: use
SecureUberspectorwhen templates are untrusted, and give the context only the simple values the template needs. It blocks documented reflection and object-creation paths; it is not an operating-system sandbox. - ✓Both: load application templates from a trusted, integrity-controlled loader. Never turn request parameters or user-writable database rows into template source.
- ✓If user-authored templates are a product requirement: render them in a separate, minimally privileged process with no network access, secrets, or writable application files; enforce CPU, memory, and wall-clock limits outside the template engine.
Treat engine restrictions as defense in depth, not as process isolation. Pin supported versions, review the exact configuration against the engine's current security guidance, and keep the renderer away from secrets and privileged application objects.
FAQ
It is a related but distinct risk. A user-controlled view name is not automatically an expression-injection flaw. Review the specific data flow into Thymeleaf or Spring Expression Language evaluation and avoid constructing expression fragments from untrusted input. Keep framework integrations patched and select views from trusted application logic.
Template authors can exercise the capabilities exposed by the engine and the data model; that does not always mean operating-system command execution. Treat template-edit permission as a sensitive code-authoring privilege, restrict it to trusted maintainers, and audit changes.
A logic-less engine such as Mustache can reduce the expression and method-call surface, but output encoding, template-source control, and the capabilities exposed by helpers still matter. If users author templates, select an engine designed for that trust model and isolate rendering rather than relying on a general-purpose JVM engine as a sandbox.
References
- Apache FreeMarker — FAQ on template uploading security
- Apache FreeMarker — SimpleObjectWrapper API
- Apache FreeMarker — TemplateClassResolver API
- Apache Velocity — developer guide
- Apache Velocity 2.3 — classpath resource loader API
- PortSwigger — server-side template injection
- CWE-1336 — Server-Side Template Injection