Server Side Template Injection in Flask with Bearer Tokens
Server Side Template Injection in Flask with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Server Side Template Injection (SSTI) in Flask becomes particularly concerning when endpoints are protected with Bearer Tokens rather than session cookies, because developers may assume token-based authentication alone limits template manipulation. SSTI occurs when an attacker can inject template code that is executed during server-side rendering, commonly via user-controlled strings passed to template functions such as render_template_string. In Flask, if an endpoint accepts a token in the Authorization header, processes input (e.g., a username, search term, or configuration key), and directly includes that input into a template string, an attacker can leverage SSTI to achieve unintended behavior.
Consider a Flask route that validates a Bearer Token and then uses a user-supplied value inside Jinja2 without sanitization:
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/greet')
def greet():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return 'Unauthorized', 401
token = auth.split(' ', 1)[1]
# Validate token (e.g., check against a list or introspection)
name = request.args.get('name', 'World')
template = 'Hello {{ ' + name + ' }}'
return render_template_string(template)
In this example, the concatenation 'Hello {{ ' + name + ' }}' allows an attacker to break out of the intended variable context and execute arbitrary template code. If the attacker sends ?name=__class__.__mro__[1].__subclasses__(), they can enumerate Python classes, potentially reaching objects that enable filesystem access, network calls, or code execution. The presence of the Bearer Token does not mitigate this because the vulnerability lies in unsafe template construction, not authentication. An attacker who cannot obtain a valid token is blocked from the endpoint, but token validation does not prevent malicious input once access is granted.
Exploitation paths vary by Flask version and configuration. In some cases, SSTI can lead to information disclosure such as reading application source files via {{ config.__class__.__init__.__globals__['__builtins__'].__class__.__mro__[1].__subclasses__() }}, or in more complex setups, Remote Code Execution (RCE) if the environment permits. Because Bearer Tokens are often used in APIs and single-page applications, developers might focus on transport security and token validation while neglecting output encoding and strict template usage, inadvertently widening the attack surface.
It is important to note that using render_template with static templates is generally safer than render_template_string with dynamic strings. However, if dynamic template strings are necessary, rigorous input validation and context-aware escaping are required. The combination of token-based auth and SSTI highlights the need to treat all user-controlled data as potentially malicious, regardless of authentication mechanisms.
Bearer Tokens-Specific Remediation in Flask — concrete code fixes
Remediation focuses on avoiding dynamic template string construction and ensuring that any variable interpolation is handled safely. The safest approach is to use render_template with predefined templates and never concatenate user input into template strings. If dynamic behavior is required, use strict allowlists and context-sensitive escaping.
Secure example using render_template with parameterized context:
from flask import Flask, request, render_template
import re
app = Flask(__name__)
@app.route('/greet')
def greet():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return 'Unauthorized', 401
token = auth.split(' ', 1)[1]
# Validate token (pseudo-validation)
if not valid_token(token):
return 'Forbidden', 403
# Safe: user input passed as context variable, not part of template syntax
name = request.args.get('name', 'World')
# Allow only alphanumeric names to prevent injection
if not re.match(r'^[A-Za-z0-9_\-\s]+$', name):
return 'Invalid name', 400
return render_template('greet.html', name=name)
def valid_token(token):
# Replace with actual token validation logic
return token.startswith('valid_prefix_')
Corresponding templates/greet.html:
<h1>Hello {{ name }}</h1>
If you must use dynamic template fragments, apply rigorous escaping and avoid executing arbitrary expressions. For example, use Jinja2’s escape filter and avoid |safe:
from flask import escape
@app.route('/search')
def search():
auth = request.headers.get('Authorization', '')
if not auth.startswith('Bearer '):
return 'Unauthorized', 401
token = auth.split(' ', 1)[1]
if not valid_token(token):
return 'Forbidden', 403
term = request.args.get('q', '')
# Escape user input before including in a template string
safe_term = escape(term)
template = 'Search results for: {{ term }}'
# Render a static template with a pre-escaped variable
return render_template('result.html', term=safe_term)
Additional measures include auditing dependencies that may render templates, enabling Flask’s TRUSTED_HOSTS appropriately, and using Content Security Policy headers where relevant. MiddleBrick scans can help identify SSTI patterns and map findings to frameworks such as OWASP API Top 10, providing remediation guidance without claiming to fix the issues automatically.
For continuous assurance, the middleBrick Pro plan supports ongoing monitoring and configurable scanning schedules, while the GitHub Action can fail builds if security scores drop below your threshold. The CLI allows you to integrate scans into scripts, and the MCP Server enables scanning APIs directly from AI coding assistants. These integrations complement secure coding practices but do not replace careful input validation and template hygiene.
Frequently Asked Questions
Can using Bearer Tokens in Flask prevent Server Side Template Injection?
What is the safest way to include dynamic values in Flask templates to avoid SSTI?
render_template with static templates and pass dynamic values as context variables. Avoid render_template_string with concatenated input, and if you must use dynamic templates, rigorously validate and escape all user-controlled data using allowlists and context-sensitive escaping.