Server-Side Template Injection (SSTI)
debt(d5/e5/b3/t7)
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.
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.
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.
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.
Also Known As
TL;DR
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
Common Misconception
Why It Matters
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
$twig->render($userInput, $data);
$twig->render('fixed_template.html.twig', ['query' => htmlspecialchars($userInput)]);