FlawOpen

flawopen.com/SSTI/详解

什么是服务端模板注入?

CWE-1336: Server-Side Template Injection参考页面
简要回答

服务端模板注入是指不可信输入被当作模板源代码解析,而不是作为数据传入。影响取决于引擎、配置和暴露的能力,可能从表达式求值到服务端代码执行不等。并非每个 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 是一回事吗?

不是。客户端模板注入在受害者浏览器中运行,可能属于 XSS 变体。SSTI 则在服务器的应用进程中求值。影响取决于引擎和暴露能力;某些配置可能允许服务端代码执行,但并非每个 SSTI 都会自动导致该结果。

自动转义能阻止 SSTI 吗?

不能。自动转义处理的是待渲染的值,并不会阻止某个值先被解析为模板语法。它可帮助防止输出中的 XSS,但不能阻止模板源代码注入。

只允许管理员编辑模板就安全吗?

应将用户提供的模板源代码视为可执行逻辑,默认不要在主应用上下文中处理。如果确实需要该功能,应使用专为受限模板设计的引擎,只暴露最少的惰性数据,并限制 CPU、内存、输出大小和执行时间。沙箱能减少访问权限,但单独使用并非完整安全边界。

参考资料

另请参阅 SSTI:模板注入的成因、影响与修复 FreeMarker 与 Velocity SSTI:安全使用模板 Jinja2 的 render_template_string() 安全吗?