flawopen.com/Vulnerabilities/Server-Side Template Injection
Server-Side Template Injection (SSTI): Root Cause and Remediation
Imagine a mail-order company with a letter template: “Dear {{ name }}, thank you for your order.” The safe process gives the printer the fixed letter and a customer name as data. The unsafe process inserts a customer’s text into the template instructions before the printer processes them. If the engine interprets that text as template syntax, it is server-side template injection. What an attacker can do next depends on the engine, its configuration, the objects exposed to the template, and the process privileges; expression evaluation does not automatically mean operating-system command execution.
Core Concepts & Subsystem Terms
- Template engine
- A server-side component such as Jinja or Twig that parses template syntax and renders output from a template plus context data.
- Template-source injection
- A flaw where externally controlled text is included in the template source before parsing, so the engine interprets the text as template syntax rather than ordinary data.
- Context data
- Values passed separately to a trusted, fixed template. Correct separation prevents a value such as
{{ 7 * 7 }}from being re-parsed as template source. - CWE-1336
- Improper Neutralization of Special Elements Used in a Template Engine. It describes unsafe handling of externally influenced input processed as template syntax.
Step-by-Step Attack Flow
Attacker-controlled text reaches a template sink
An input field, stored profile value, or other externally influenced string reaches code that creates or compiles a template. The vulnerable condition is source construction, not merely rendering a value in a template.
Application mixes data with template instructions
The application interpolates the untrusted string into template source (for example, an f-string passed to render_template_string) instead of using a static template and a separate context value.
The engine parses the combined source
The selected template engine interprets recognized expressions according to its language and configuration. A harmless arithmetic expression can demonstrate evaluation; this alone does not prove access to files, secrets, or operating-system commands.
Impact depends on engine capabilities and deployment
Further impact depends on accessible objects and functions, sandbox behavior, engine-specific restrictions, and the web process privileges. RCE is a possible outcome in some configurations, not an automatic consequence of every SSTI.
The Mechanism & Root Cause
SSTI occurs when an application lets externally influenced text become part of the template program before parsing. In Flask/Jinja, the vulnerable pattern is building a new source string from request data and passing it to render_template_string(); autoescaping addresses HTML output encoding and does not turn untrusted template source into data. The safer pattern loads a fixed template and passes the value separately as context. If users must author templates, treat that as a separate feature: expose a minimal data model, use a sandbox designed for that use, and isolate rendering with resource limits. A sandbox is defense in depth, not permission to expose secrets or powerful application objects.
# VULNERABLE: request data becomes template source before parsing.
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route("/greet")
def greet():
name = request.args.get("name", "Guest")
# A value such as {{ 7 * 7 }} is interpreted by the template engine.
source = f"<h1>Welcome, {name}!</h1>"
return render_template_string(source)
# HARDENED: fixed template source; request data stays a context value.
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route("/greet")
def greet():
name = request.args.get("name", "Guest")
return render_template("greet.html", name=name)
# templates/greet.html is trusted, static source:
# <h1>Welcome, {{ name }}!</h1>
The Attack & Exploit Sequence
- 1Source: An externally influenced value reaches a template-construction path.
- 2Unsafe composition: The application places that value into template source rather than passing it separately as context data.
- 3Interpretation: The engine parses the combined source. A harmless arithmetic expression may confirm evaluation in an authorized test environment.
- 4Conditional impact: Further access depends on engine-specific behavior, exposed objects, configuration, and process privileges; do not infer RCE from expression evaluation alone.
Defensive Engineering & Prevention Rules
- ✓Keep source fixed: Load trusted templates from files or a controlled loader; pass user input only as context values. Do not concatenate request data into template source.
- ✓Distinguish SSTI from HTML injection: HTML autoescaping protects output contexts; it does not repair a flow where untrusted input is parsed as template source.
- ✓Constrain user-authored templates: If user-editable templates are a required feature, use the engine’s sandbox for that purpose, expose only a minimal data model, and render in a resource-limited isolated worker. Follow the engine’s security notes.
- ✓Limit blast radius: Run the renderer as a non-root service with minimal filesystem, network, and secret access. Treat this as containment, not the primary fix.
- ✓Add regression tests: Pass a harmless marker such as
{{ 7 * 7 }}as data and verify it remains data in the fixed path; test that templates come only from the trusted loader.