Skip to main content
Offensive360
Home / Knowledge Base / Server-Side Template Injection (SSTI)
Critical CWE-94 A03:2021 Injection

Server-Side Template Injection (SSTI)

SSTI occurs when user input is embedded in server-side templates and evaluated as code, leading to remote code execution. Learn detection and prevention across Jinja2, Twig, Razor, and more.

Affects: PythonPHPJavaRubyJavaScriptC#

What is Server-Side Template Injection?

Server-Side Template Injection (SSTI) occurs when an application embeds user-controlled data directly into a template string that is subsequently rendered by a server-side template engine. Unlike reflected XSS, where injected content is interpreted by the browser, SSTI is evaluated on the server — often giving the attacker full Remote Code Execution (RCE) capability.

Template engines like Jinja2 (Python), Twig (PHP), Pebble (Java), and Razor (C#) provide powerful expression syntax for dynamic content. When user input is placed into the template itself (not just passed as a safe variable), an attacker can invoke built-in functions to read files, execute OS commands, or pivot further into the infrastructure.

How exploitation works

An application renders a greeting using a user-supplied name:

# VULNERABLE: Python / Jinja2
from jinja2 import Template
name = request.args.get('name')
template = Template("Hello, " + name + "!")  # User input in template string
return template.render()

The attacker sends: name={{7*7}} and receives Hello, 49! — confirming template evaluation. From there, exploitation escalates:

# Read /etc/passwd
{{''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']('cat /etc/passwd').read()}}

Vulnerable code examples

Python / Jinja2

# VULNERABLE: User input concatenated into template string
@app.route('/greet')
def greet():
    name = request.args.get('name', 'World')
    html = render_template_string("<h1>Hello, " + name + "</h1>")
    return html

PHP / Twig

// VULNERABLE: Rendering user input as a template
$loader = new \Twig\Loader\ArrayLoader([]);
$twig = new \Twig\Environment($loader);
$template = $twig->createTemplate("Hello " . $_GET['name'] . "!");
echo $template->render([]);

Secure code examples

Python / Jinja2 — pass data as context variable

# SECURE: User input passed as a template variable, never embedded in template string
@app.route('/greet')
def greet():
    name = request.args.get('name', 'World')
    # Template is a static string; name is injected as a safe context variable
    return render_template_string("<h1>Hello, {{ name }}</h1>", name=name)

Java / Thymeleaf — use model attributes

// SECURE: User data bound to model, never concatenated into template expression
@GetMapping("/greet")
public String greet(@RequestParam String name, Model model) {
    model.addAttribute("name", name); // Thymeleaf auto-escapes th:text values
    return "greet"; // References static templates/greet.html
}
<!-- greet.html — static template, data via th:text (auto-escaped) -->
<h1>Hello, <span th:text="${name}"></span>!</h1>

What Offensive360 detects

  • User input in template strings — Calls to render_template_string(), Template(), createTemplate(), or equivalent where the template argument includes tainted data
  • Dynamic template construction — String concatenation or interpolation used to build template expressions
  • Unsafe Razor expressions@Html.Raw() or dynamic evaluation in Razor with user-supplied content
  • Handlebars/Mustache triple braces{{{userInput}}} that bypasses HTML escaping in JavaScript template engines

Remediation guidance

  1. Never embed user input in template strings — Always pass user data as context/model variables. Template engines auto-escape variable values; they do not evaluate them as code.

  2. Use static template files — Load templates from the filesystem (render_template('greet.html', name=name)) rather than constructing template strings at runtime.

  3. Enable sandbox mode — Some engines (Jinja2, Twig) offer sandbox environments that restrict access to dangerous built-in objects. Enable and configure appropriately.

  4. Apply output encoding — Ensure template output is correctly encoded for the rendering context (HTML, JavaScript, URL).

  5. Audit third-party template rendering — Email generation, PDF rendering, and notification systems are common vectors for SSTI that are overlooked during code review.

References

By Offensive360 Security Research Reviewed: March 2026

Detect Server-Side Template Injection (SSTI) automatically

Run Offensive360 SAST on your codebase to find this and 100+ other vulnerabilities.