FlawOpen

flawopen.com/SSTI/FreeMarker & Velocity

Are FreeMarker and Velocity Templates Safe with User Input?

CWE-1336: Server-Side Template InjectionReference page
Short answer

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.

VULNERABLE
// 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);
FIXED
// 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

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

Is Thymeleaf affected too?

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.

We only let administrators edit templates. Is that acceptable?

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.

Which engine is safest for user-supplied templates?

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

See also Server-Side Template Injection (SSTI): Causes and Fixes Is Jinja2's render_template_string() safe? What is server-side template injection?