CWE-1336, Improper Neutralization of Special Elements Used in a Template Engine, is the weakness behind server-side and client-side template injection. External input reaches a template engine in a form it treats as an expression or directive, and the engine runs it.
It affects any app that renders pages, emails or messages from templates: Java, PHP, Python and JavaScript projects are all listed in the record.
How it happens
MITRE names engines such as Twig, Jinja2, Pug, Java Server Pages, FreeMarker, Velocity, ColdFusion and Smarty, and notes that some CMS products and even PHP itself work this way. These engines have their own expression languages. If an attacker can influence text before the engine processes it, they can invoke arbitrary expressions.
The standard probe is an expression like {{7*7}}. If the output shows 49 instead of the literal text, the input was evaluated. Template engines run on both the server and the client, so both sides can be affected. The mechanics differ, but the mistake is the same. MITRE adds that XSS-style attacks sometimes work too, which can hide the real root cause.
The difference between safe and unsafe comes down to whether input is data or template source.
# Vulnerable: user input becomes part of the template source
from jinja2 import Template
def greet(name):
return Template("Hello " + name).render()
# name = "{{7*7}}" renders "Hello 49"
# Safer: input is passed as data, template source is fixed
from jinja2 import Template
def greet(name):
return Template("Hello {{ name }}").render(name=name)
# name = "{{7*7}}" renders the literal text
For templates you must build from untrusted authors, use a sandboxed environment:
from jinja2.sandbox import SandboxedEnvironment
env = SandboxedEnvironment()
out = env.from_string(user_template).render(name="Ada")
The record lists CVEs across several stacks:
- CVE-2024-34359: a chain in which Python bindings for an LLM library do not use a sandboxed environment when parsing a template and building a prompt, allowing jinja2 server-side template injection and code execution.
- CVE-2017-16783: server-side template injection in a content management server.
- CVE-2020-9437: client-side template injection in an authentication and identity management product.
- CVE-2020-12790: server-side template injection using a Twig template.
The first is a good reminder that prompt templates are templates. If a library renders a prompt through a template engine, the same rules apply.
What an attacker gains
MITRE lists integrity impact and execution of unauthorized code or commands. In many engines template expressions can reach objects and functions of the host language, so a probe that prints 49 can often be escalated into running commands on the server. The exact reach depends on the engine and its configuration.
How to find it
MITRE says static analysis (SAST) can find some instances through data flow analysis.
Practical checks:
- Search for code that concatenates or formats user input into a string and passes that string to a template compile or render function. The red flag is a render call whose template argument is not a constant.
- Test inputs with the engine's expression syntax, such as
{{7*7}}or the equivalent for your stack, in every field that ends up in a rendered page or message. Check for evaluated output. - Check which engines and versions your dependencies bring in, and whether any known advisory applies. The free scanner at gold.safeguard.sh/scan runs in the browser with no login and covers up to 100 dependencies.
How to fix it
From the record:
- Choose a template engine that offers a sandbox or restricted mode, or at least limits the power of expressions, function calls and commands.
- Use that sandbox or restricted mode when it is available.
Beyond those two, the code example above shows the structural fix: keep template source constant and pass input only as variables. If users need to author templates, treat that as an untrusted code feature, sandbox it and keep it away from sensitive objects.
Try it yourself
See how a code scan flags a render call whose template comes from input.
Click through it at your own pace, or open it full screen.
Quick check
What does seeing 49 after submitting the probe mean?
The engine evaluated the input as an expression, which indicates template injection.
Can template injection happen in the browser?
Yes. MITRE notes template engines run on both server and client, and both can be affected.
Does escaping HTML fix it?
Not by itself. The problem is input being treated as template source. Keep source constant, pass data as variables and sandbox where needed.