HIGH server side template injectionflask

Server Side Template Injection in Flask

How Server Side Template Injection Manifests in Flask

Server Side Template Injection (SSTI) occurs when an attacker can inject template code into a template engine that is then executed on the server. In Flask applications, this vulnerability primarily stems from the use of Jinja2, Flask's default templating engine. The core issue arises when user-controlled input is directly passed to template rendering functions without proper sanitization or when template strings are constructed dynamically.

The most common Flask-specific pattern is the use of render_template_string() with unsanitized request data. For example:

from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.route('/greet')
def greet():
    # VULNERABLE: user input directly in template string
    name = request.args.get('name', 'Guest')
    template = f'<h1>Hello, {name}!</h1>'
    return render_template_string(template)

# Attacker request: /greet?name={{ config.items() }}

Here, an attacker can inject Jinja2 expressions like {{ config.items() }} to dump the application's configuration, including potentially secret keys. More severe exploits leverage Jinja2's ability to access Python objects and methods. For instance, {{ ''.__class__.__mro__[1].__subclasses__() }} can list all available classes, leading to remote code execution if the environment is not sandboxed. This is particularly dangerous in Flask because the template engine runs in the same context as the application, giving attackers access to os, subprocess, and other sensitive modules.

SSTI can also occur through render_template() if template filenames are user-controlled or if templates themselves include user input in unsafe ways (e.g., using the | safe filter). While Jinja2's autoescaping protects against XSS, it does not prevent SSTI when the template structure is compromised. The vulnerability is categorized under OWASP API Top 10 2023 as A03:2021 – Injection and has been the basis for real-world exploits, such as CVE-2019-12494, a sandbox escape in Jinja2.

Flask-Specific Detection

Detecting SSTI in Flask requires examining both code patterns and runtime behavior. The primary code smell is any use of render_template_string() with data from HTTP requests, cookies, or other external sources. Similarly, render_template() calls where the template name or path is constructed from user input (e.g., render_template(request.args.get('template'))) are risky because they allow template injection if an attacker can place a malicious template in the filesystem (via file upload vulnerabilities, for instance).

Dynamic template loading from databases or external services also introduces SSTI risk if the template content is user-controlled. For example:

@app.route('/view')
def view():
    template_id = request.args.get('id')
    template_content = db.get_template(template_id)  # if template_content is user-provided
    return render_template_string(template_content)  # VULNERABLE

Black-box testing involves sending payloads that trigger observable side effects. Classic SSTI payloads include mathematical expressions ({{ 7*7 }}), object introspection ({{ config }}'), and time-based delays ({% for c in ''.__class__.__mro__[1].__subclasses__() %}{% if c.__name__ == 'catch_warnings' %}{{ c.__init__.__globals__['os'].popen('sleep 10').read() }}{% endif %}{% endfor %}). A successful exploit is indicated by the payload's effect appearing in the response (e.g., 49 for {{ 7*7 }}) or by error messages revealing class structures.

middleBrick automates this detection during its Input Validation scan. When you submit a Flask endpoint URL, middleBrick systematically injects a battery of Jinja2-specific payloads—including those that test for sandbox escapes, config disclosure, and command execution—into all detectable input vectors (query parameters, headers, JSON bodies). It then analyzes responses for execution artifacts, such as computed values, Python tracebacks, or unique strings. The scanner maps any confirmed SSTI to the Input Validation category with a severity rating and provides remediation guidance specific to Flask's templating patterns.

Flask-Specific Remediation

The most effective remediation is to eliminate the use of render_template_string() with any user-controlled data. Instead, use static template files and pass user input as context variables, which are autoescaped by default in Jinja2. For example, refactor the vulnerable /greet endpoint to:

@app.route('/greet')
def greet():
    name = request.args.get('name', 'Guest')
    # SAFE: user input is a variable, not template code
    return render_template('greeting.html', name=name)

In templates/greeting.html, use {{ name }} without the | safe filter. Autoescaping ensures that any HTML or Jinja2 syntax in name is rendered as plain text, not executed.

If dynamic templates are unavoidable (e.g., a CMS where users design pages), isolate the template rendering in a sandboxed environment. Flask allows you to create a custom Jinja2 environment with sandboxing:

from jinja2.sandbox import SandboxedEnvironment

@app.route('/dynamic')
def dynamic():
    template_content = get_trusted_template()  # Must come from a trusted source
    env = SandboxedEnvironment()
    template = env.from_string(template_content)
    return template.render()

Important: The sandbox is not foolproof; historical CVEs like CVE-2019-12494 demonstrated sandbox escapes. Only use this approach if you fully trust the template source. For user-generated templates, consider a whitelist of allowed template names or a completely different design (e.g., storing user content as data, not code).

Additional safeguards include:

  • Never use | safe on user input in templates.
  • Keep Jinja2 updated to patch known sandbox vulnerabilities.
  • Validate and sanitize any input that influences template selection or content.

Finally, incorporate automated scanning into your development lifecycle. With middleBrick's GitHub Action, you can add SSTI checks to your CI/CD pipeline and fail builds if a scan detects template injection risks. The Pro plan's continuous monitoring also alerts you if new SSTI findings appear in deployed APIs.

Frequently Asked Questions

Can I use middleBrick to test my Flask API for SSTI?
Yes. middleBrick's Input Validation check includes specific tests for Server Side Template Injection in Flask applications. When you scan an endpoint, it automatically sends Jinja2 payloads to all input parameters and analyzes responses for signs of template execution. The resulting report will flag any SSTI findings with severity, evidence, and Flask-specific remediation guidance.
How does middleBrick's SSTI detection differ from generic scanners?
While many scanners test for general injection, middleBrick's SSTI detection is tailored to template engines like Jinja2. It uses payloads that exploit Jinja2's object introspection and sandbox escape techniques (e.g., accessing config, __class__, and __subclasses__). The scanner also correlates these findings with Flask-specific patterns, such as render_template_string usage, and maps them to OWASP API Top 10 and compliance frameworks like PCI-DSS.