Application Security · Learn

What is Jinja2 template injection?

When a Flask or Python app builds a Jinja2 template out of user input instead of passing that input as data, the attacker's template expressions run on the server, often ending in code execution.

Application Security · LearnApplication Penetration Testing Download PDF
TL;DR

Jinja2 template injection is server-side template injection (SSTI) in the Jinja2 engine used by Flask and other Python apps. It happens when user input is concatenated into the template source and rendered, so {{ ... }} expressions from the attacker execute on the server. From there an attacker reads the Flask config (which often holds secrets), walks Python objects, and reaches OS command execution. The fix is to pass user input as template data, never as template source.

By Shubham Khandare, Delivery Manager, SecureLayer7Updated

What Jinja2 template injection is

Jinja2 renders templates by evaluating expressions inside {{ }} and {% %}. The vulnerability appears when the template string itself is built from user input, for example render_template_string('Hello ' + name) or an f-string that drops user data into the template body. Now the user controls template syntax, not just template data, so their expressions are evaluated with the privileges of the app. This is a specific, high-impact case of server-side template injection (SSTI).

How it works and example

The standard probe is a math expression only a template engine would evaluate: send {{7*7}}, and if the response contains 49, input is being rendered as a template. On Jinja2 an attacker then escalates from expression evaluation to code execution by reaching Python builtins through object attributes, for example {{ config.__class__.__init__.__globals__['os'].popen('id').read() }} or {{ cycler.__init__.__globals__.os.popen('id').read() }}. Simply rendering {{ config }} often dumps the Flask configuration, including SECRET_KEY and database credentials. Payloads are shown here for defensive testing.

How to fix it

Never build a template out of user input. Keep templates as static files and pass user data through the render context, for example render_template('page.html', name=name), so input is treated as data and escaped, not as template code. If you truly must render user-supplied templates, use Jinja2's SandboxedEnvironment, but treat it as defence in depth because sandbox escapes have existed; the safer stance is not to render untrusted templates at all. Note that autoescaping protects against XSS in the output, it does not prevent SSTI.

References

  1. [1]OWASP Web Security Testing Guide(OWASP)
  2. [2]MITRE CWE-1336: Server-Side Template Injection(MITRE CWE)
  3. [3]Jinja2 sandbox documentation(Pallets)
  4. [4]OWASP Top 10(OWASP)
Related terms

If any user input reaches a template string, Jinja2 SSTI is in your threat model. Talk to a security expert about testing it end to end.

FAQ

Jinja2 SSTI, asked often

Scope an engagement

Test your application for SSTI and 30 or more other classes.

We test applications for this and 30 or more other vulnerability classes, and ship findings with a reproducible attack, the trust boundary that failed, and a fix a developer can implement.