← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Server-Side Template Injection (SSTI)

Security CWE-1336 OWASP A3:2021 CVSS 9.8 PHP 5.0+ Advanced
debt(d5/e5/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and psalm as tools, both specialist SAST tools. The code pattern (user input passed as the template string itself) is not caught by a default linter or compiler — it requires a targeted semgrep rule or static analysis pass to identify user-controlled template rendering paths.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix says to never render user input as a template string and instead pass data as variables, but this architectural pattern change (separating template structure from user data) can require auditing every call site in the codebase where render() is invoked, touching multiple files and potentially restructuring how templates are loaded — not a single-line swap.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). The applies_to scope is web contexts only, and SSTI is a vulnerability class rather than a pervasive structural choice. Once the fix is applied (never pass user input as the template), the burden is limited. It doesn't shape the entire codebase architecture, but developers working with templating code must stay aware of it.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states that developers treat SSTI as a read-only information disclosure issue, when in reality it enables full RCE via template engine sandbox escapes. This contradicts the mental model that template rendering is 'safer' than eval or SQL queries, and the common mistake of confusing output encoding (XSS prevention) with template injection compounds the danger — two similar-seeming concepts behave very differently.

About DEBT scoring →

Also Known As

Server-Side Template Injection template injection SSTI

TL;DR

User input is embedded directly into a server-side template, allowing arbitrary code execution on the server.

Explanation

SSTI occurs when an application passes unsanitised user data into a template engine (Twig, Smarty, Blade) which then evaluates it as template syntax. An attacker who discovers the injection point can escalate to full remote code execution by exploiting the template engine's built-in object access. In PHP, Twig in sandbox mode mitigates the risk; never pass raw user input to Environment::createTemplate() or render().

How It's Exploited

An attacker submits a template expression such as {{7*7}} in a search field. If the response shows 49, they know the template engine is evaluating input and can escalate to {{''.__class__.__mro__[2].__subclasses__()}} or similar gadget chains to execute OS commands.

Common Misconception

Template injection is just a read-only information disclosure issue. Most template engines (Twig, Smarty, Blade) expose functionality that allows full RCE when injection is possible — attackers can call system functions or traverse the object graph to execute arbitrary code.

Why It Matters

Server-side template injection is effectively remote code execution within the template engine's context — often providing access to the underlying OS through template sandbox escapes.

Common Mistakes

  • Passing user input as the template string to render() instead of as a variable to a fixed template.
  • Using Twig with autoescape disabled and reflecting user input into the template.
  • Not sandboxing template rendering when user-defined templates are a feature of the application.
  • Confusing output encoding (which prevents XSS) with template injection (which requires not including user input in the template structure).

Code Examples

✗ Vulnerable
$twig->render($userInput, $data);
✓ Fixed
$twig->render('fixed_template.html.twig', ['query' => htmlspecialchars($userInput)]);

Added 15 Mar 2026
Edited 12 Jun 2026
Views 49
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 2 pings S 1 ping S 2 pings M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 2 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Amazonbot 10 SEMrush 5 Scrapy 5 ChatGPT 4 Google 2 Unknown AI 2 Perplexity 1 Claude 1 Bing 1 PetalBot 1
crawler 29 crawler_json 2 pre-tracking 1
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Medium
⚡ Quick Fix
Never render user input as a template string — pass data as template variables, not as the template itself; sandbox the template engine if user templates are required
📦 Applies To
PHP 5.0+ web laravel symfony twig
🔗 Prerequisites
🔍 Detection Hints
Twig::render($userInput) or eval with template string containing user data; user-controlled template path
Auto-detectable: ✓ Yes semgrep psalm
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✗ Manual fix Fix: High Context: File Tests: Update
CWE-94


✓ schema.org compliant