flawopen.com/SSTI/Explained

What is server-side template injection?

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

It happens when user input is concatenated into the template instead of passed in as data. Template engines evaluate expressions, so controlling the template means controlling an expression evaluator — which in most engines reaches the language runtime and becomes remote code execution.

VULNERABLE — input builds the template
# Python / Jinja2
template = f"Hello {name}!"
return render_template_string(template)

# Input: {{ 7*7 }}  → renders "49"
# That confirms evaluation, and from
# there the object graph is reachable.

// Java / Freemarker
String tpl = "Welcome " + userName;
new Template("t", tpl, cfg)
    .process(model, out);
FIXED — input is data
# The template is a fixed string the
# developer wrote. The user value is
# passed in as a variable.
return render_template(
    "greeting.html", name=name
)

# Or with a literal template string:
return render_template_string(
    "Hello {{ name }}!", name=name
)

// Java equivalent: template from file,
// user value in the model map.

The distinction that matters

Every template engine has two inputs: the template and the context data. The template is a program; the data is values that program reads. SSTI is the mistake of letting user input cross from the second category into the first.

This is the same structural error as SQL injection, where data is concatenated into query text, and as Log4Shell, where data reached an expression evaluator through a log message. In each case the fix has the same shape: keep the program fixed and pass untrusted values through a parameter channel.

Why it usually reaches code execution

Template expression languages are designed to navigate object graphs — read properties, call methods, iterate collections. Given that, an attacker walks from any exposed object to the language runtime: in Python, through an object's class and its inheritance chain to reach module imports; in Java, through reflection to Runtime.getRuntime(). Sandboxed modes exist in several engines, and several have had sandbox escapes.

How to detect it

{{7*7}} ${7*7} <%= 7*7 %> #{7*7}

If a response contains 49 rather than the literal text, the input is being evaluated. The differing syntaxes also fingerprint the engine — {{ }} for Jinja2/Twig/Handlebars, ${ } for Freemarker and Thymeleaf, <%= %> for ERB and EJS.

FAQ

Is this the same as XSS?

No. Client-side template injection runs in the victim's browser and is an XSS variant. SSTI runs on your server, in your application process, with your application's privileges — the impact is typically full compromise of the host rather than the session.

Does auto-escaping prevent it?

No. Auto-escaping sanitises values being rendered; it does not stop a value from being parsed as template syntax in the first place. It protects against XSS in output, not against injection into the template source.

Is it safe if I only allow users to edit templates as an admin feature?

Treat template editing as equivalent to granting code execution on the server, and gate it accordingly. A "logic-less" engine such as Mustache is a much better choice for user-supplied templates than a general-purpose one.

References