Server-Side Template Injection (SSTI)

When string formatting escalates to Remote Code Execution.

The idea

Template engines (like Jinja2, Twig, or EJS) are used to dynamically generate HTML. They securely inject variables into a static template. However, if a developer dynamically concatenates user input directly into the template string itself before rendering it, an attacker can inject template directives (e.g. `{{ ... }}`) that the engine will execute.

Because template engines have deep access to the runtime environment, SSTI almost always leads to complete server takeover (Remote Code Execution).

Step 1: Normal usage. The template receives a variable.

How it works (Pass variables, don't format strings)

Never use `f-strings` or `.format()` to build a template string out of user input. Always pass user input into the template engine as a context variable.

# VULNERABLE: Modifying the template itself
from flask import render_template_string

@app.route("/hello")
def hello():
    name = request.args.get('name')
    # DANGER: We are concatenating user input directly into the template string!
    template = f"<h1>Hello {name}</h1>"
    return render_template_string(template)

# SECURE: Passing as a variable
@app.route("/hello")
def hello():
    name = request.args.get('name')
    # The template is static. The variable is passed safely via context.
    template = "<h1>Hello {{ user_name }}</h1>"
    return render_template_string(template, user_name=name)

Watch out for

Worked example

A marketing tool allows users to customize their email footers. The backend uses Ruby's ERB templating. A user saves their footer as `<%= system('cat /etc/passwd') %>`. When the backend renders the email to send it, the ERB engine executes the `system()` call, extracting the password file and placing it straight into the outgoing email.

Check yourself

An attacker wants to test if a webpage is vulnerable to Server-Side Template Injection. They enter `{{ 7 * 7 }}` into a search box. What response would confirm the vulnerability?

No. While an error might indicate a problem, it doesn't confirm the template engine actually evaluated the payload.
Correct! If the output shows 49, it proves the server-side template engine parsed and executed the mathematical expression, confirming SSTI.