flawopen.com/SSTI/详解
什么是服务端模板注入?
服务端模板注入是指不可信输入被当作模板源代码解析,而不是作为数据传入。影响取决于引擎、配置和暴露的能力,可能从表达式求值到服务端代码执行不等。并非每个 SSTI 都能导致代码执行。
# Python / Jinja2 template = f"Hello {name}!" return render_template_string(template) # Input: {{ 7*7 }} renders as "49" # This confirms expression evaluation; # impact still depends on engine access. // Java / Freemarker String tpl = "Welcome " + userName; new Template("t", tpl, cfg) .process(model, out);
# The template is a fixed string the # developer wrote. The user value is # passed in as a variable. return render_template( "greeting.html", name=name ) # Or with a literal template string: return render_template_string( "Hello {{ name }}!", name=name ) // Java equivalent: template from file, // user value in the model map.
关键区别
每个模板引擎都有两类输入:模板和上下文数据。模板是程序;数据是程序读取的值。SSTI 就是允许用户输入从数据类别跨越到模板源代码类别。
这与 SQL 注入的结构性错误相同:把数据拼接进查询文本;也类似 Log4Shell,数据通过日志消息进入表达式求值器。修复原则一致:程序保持固定,不可信值通过参数通道传递。
何时影响可能包括代码执行
如果模板引擎向模板暴露了强大的对象或方法,攻击者可能触及应用的敏感能力。是否能执行代码取决于引擎、版本、配置、沙箱和暴露的数据模型。其他影响还包括信息泄露或资源耗尽。应将用户可控的模板源代码视为活动逻辑;若不限制可访问对象及渲染资源,不要只依赖沙箱。
如何检测
{{7*7}}
${7*7}
<%= 7*7 %>
#{7*7}
如果响应显示 49 而不是原样文本,说明输入被当作模板语法求值。在获准的测试环境中,这类简短算术探针可用于发现求值行为;语法因引擎而异,不能仅凭它可靠识别引擎。
常见问题
不是。客户端模板注入在受害者浏览器中运行,可能属于 XSS 变体。SSTI 则在服务器的应用进程中求值。影响取决于引擎和暴露能力;某些配置可能允许服务端代码执行,但并非每个 SSTI 都会自动导致该结果。
不能。自动转义处理的是待渲染的值,并不会阻止某个值先被解析为模板语法。它可帮助防止输出中的 XSS,但不能阻止模板源代码注入。
应将用户提供的模板源代码视为可执行逻辑,默认不要在主应用上下文中处理。如果确实需要该功能,应使用专为受限模板设计的引擎,只暴露最少的惰性数据,并限制 CPU、内存、输出大小和执行时间。沙箱能减少访问权限,但单独使用并非完整安全边界。
参考资料
- CWE-1336 — Improper Neutralization of Special Elements Used in a Template Engine
- PortSwigger Web Security Academy — server-side template injection
- OWASP Testing Guide — testing for SSTI
- Is Jinja2's render_template_string() safe?
- Are Freemarker and Velocity templates safe with user input?
- Jinja documentation — Sandbox security considerations