Skip to content

Server-Side Template Injection Prevention Cheat Sheet / 服务器端模板注入防护清单

Introduction / 介绍

Server-Side Template Injection (SSTI) happens when a server-side template engine evaluates untrusted input as template code instead of rendering it as data (Common Weakness Enumeration CWE-1336). Engines such as Jinja2, Twig, and FreeMarker have their own expression languages, so injected syntax runs with the engine's access to application objects. Depending on the engine and its configuration, the impact ranges from reading sensitive data and files to remote code execution.

服务器端模板注入(SSTI)发生在服务器端模板引擎将不受信任的输入作为模板代码评估,而不是将其作为数据渲染(常见弱点枚举 CWE-1336)。例如 Jinja2、Twig 和 FreeMarker 等引擎都有自己的表达式语言,因此注入的语法会以引擎对应用程序对象的访问权限运行。根据引擎及其配置,影响范围从读取敏感数据和文件到远程代码执行不等。

Untrusted input crosses into template code in three ways:

不受信任的输入以三种方式进入模板代码:

  • Input becomes template source. Untrusted data, such as request parameters, stored user content, or third-party files, is concatenated into a template string, even inside an expression like "Hello {{" + name + "}}", or passed to a string-to-template API such as Jinja2's from_string() or Flask's render_template_string().
    中文:输入成为模板源。 不受信任的数据,如请求参数、存储的用户内容或第三方文件,被连接到模板字符串中,即使在像 "Hello {{" + name + "}}" 这样的表达式内部,或者传递给字符串到模板的 API,如 Jinja2 的 from_string() 或 Flask 的 render_template_string()。
  • A template evaluates input as code. A fixed template passes a value to a feature that parses strings as template code, such as FreeMarker's ?interpret and ?eval built-ins or Twig's template_from_string() function.
    中文:模板将输入评估为代码。 一个固定的模板将值传递给一个将字符串解析为模板代码的功能,如 FreeMarker 的 ?interpret 和 ?eval 内置函数或 Twig 的 template_from_string() 函数。
  • Input chooses the template. User input selects a template name, path, or include target, which can make the engine load files it should not.
    中文:输入选择模板。 用户输入选择模板名称、路径或包含目标,这可能导致引擎加载它不应加载的文件。

Do not try to filter template syntax out of input. Syntax differs between engines and contexts, so a denylist will miss payloads.

不要尝试从输入中过滤模板语法。语法在不同引擎和上下文中不同,因此黑名单会错过有效载荷。

This cheat sheet covers prevention. To test a running application, use the OWASP Web Security Testing Guide (WSTG-INJT-18).

本清单涵盖防护措施。要测试运行中的应用程序,请使用 OWASP 网络安全测试指南(WSTG-INJT-18)。

Prevention / 预防

Treat templates as code / 将模板视为代码

Keep templates with the application source, review changes to them like code, and never build them from untrusted data. Do not let untrusted data choose a template name, path, or include target either: map the user's choice to a fixed list of template names on the server. If users must author templates, follow the User-Supplied Templates section below.

将模板与应用程序源代码一起保存,像代码一样审查它们的更改,并且永远不要从不受信任的数据中构建它们。不要让不受信任的数据选择模板名称、路径或包含目标:将用户的选项映射到服务器上固定列表的模板名称。如果用户必须创建模板,请遵循下面的“用户提供的模板”部分。

Pass untrusted input only as data / 仅将不受信任的输入作为数据传递

Render a fixed template and pass user values as named variables, the same way a parameterized query keeps data out of SQL.

渲染一个固定的模板,并将用户值作为命名变量传递,就像参数化查询将数据排除在 SQL 之外一样。

Keep the render context minimal / 保持渲染上下文最小化

A template can read everything in its render context and, depending on the engine, call methods on the objects you pass. Pass only the values a template needs. Do not pass secrets, configuration, service clients, or objects whose methods have side effects. This limits what an injected template can reach, but it does not stop code execution in an engine that is not sandboxed.

模板可以读取其渲染上下文中的所有内容,并且根据引擎,可以调用传递给它的对象的方法。只传递模板需要的值。不要传递秘密、配置、服务客户端或具有副作用的方法的对象。这限制了注入的模板可以访问的内容,但这并不能阻止在非沙箱引擎中执行代码。

Prefer the least powerful engine / 优先使用最无权的引擎

Choose an engine that limits the power of its expressions, function calls, or commands, such as a logic-less engine like Mustache, unless you need more. Custom helpers and lambdas you register still run as application code.

选择一个限制其表达式、函数调用或命令的引擎,例如 Mustache 这样的无逻辑引擎,除非你需要更多。你注册的自定义辅助函数和 lambda 表达式仍然作为应用程序代码运行。

Keep output escaping on / 保持输出转义开启

Auto-escaping helps prevent cross-site scripting in HTML output. It does not prevent SSTI, because escaping applies to the values a template prints, not to template code the engine has already parsed. Keep it enabled and never mark untrusted values as safe.

自动转义有助于防止 HTML 输出中的 跨站脚本。它不能防止 SSTI,因为转义仅适用于模板打印的值,而不是引擎已解析的模板代码。请保持开启状态,并永远不要将不受信任的值标记为安全。

Find vulnerable template code / 查找有漏洞的模板代码

  • Inventory every place the application renders templates, including email and notification bodies, PDF and report generation, prompts for large language models, and every feature that lets users create or edit templates. Treat templates that ship in third-party files as templates you did not write.
    中文:列出应用程序渲染模板的所有位置,包括电子邮件和通知正文、PDF 和报告生成、大型语言模型的提示,以及任何允许用户创建或编辑模板的功能。将第三方文件中提供的模板视为你未编写的模板。
  • Search for string-to-template APIs and features, such as Jinja2's from_string(), Flask's render_template_string(), Twig's createTemplate() and template_from_string(), and FreeMarker's Template constructor and ?interpret and ?eval built-ins. Also search for code that builds template source or template names from input.
    中文:搜索字符串到模板的 API 和功能,例如 Jinja2 的 from_string()、Flask 的 render_template_string()、Twig 的 createTemplate() 和 template_from_string(),以及 FreeMarker 的 Template 构造函数和 ?interpret 和 ?eval 内置功能。还要搜索构建模板源或模板名称的代码。
  • For each call site, confirm that untrusted data reaches only render variables and that no template passes it to a feature that evaluates strings as code.
    中文:对于每个调用点,确认不受信任的数据仅到达渲染变量,并且没有模板将其传递给将字符串作为代码评估的功能。

Engine Configuration / 引擎配置

Frameworks can change engine defaults, so check the effective settings in your application. For other engines, consult their documentation for restrictions on untrusted templates.

框架可以更改引擎默认设置,因此请检查您应用程序中的实际设置。对于其他引擎,请查阅它们的文档以了解对不受信任模板的限制。

Engine / 引擎 Restrictions for untrusted templates / 不受信任模板的限制
Jinja2 Use SandboxedEnvironment, or ImmutableSandboxedEnvironment to also block changes to lists, sets, and dictionaries. Restrict attributes further by overriding is_safe_attribute(), and decorate dangerous methods with unsafe().
使用沙箱环境或ImmutableSandboxedEnvironment来阻止对列表、集合和字典的更改。通过重写is_safe_attribute()进一步限制属性,并使用unsafe()装饰危险的方法。
Twig Twig treats templates as trusted code, so its sandbox is the only boundary for untrusted authors. Give the sandbox its own environment and a strict SecurityPolicy that allowlists the tags, filters, functions, tests, methods, and properties templates may use. The Sandbox class requires Twig 3.29 or later. Never allow template_from_string() in sandboxed templates.
Twig将模板视为可信代码,因此其sandbox是未经验证的作者的唯一边界。为沙箱提供自己的环境和严格的SecurityPolicy,允许列表中包含模板可能使用的标签、过滤器、函数、测试、方法和属性。Sandbox类需要Twig 3.29或更高版本。绝不允许在沙箱模板中使用template_from_string()。
FreeMarker Follow the FAQ on uploaded templates: set the ?new class resolver to ALLOWS_NOTHING_RESOLVER, not SAFER_RESOLVER; keep ?api disabled (the default), restrict member access with SimpleObjectWrapper or a WhitelistMemberAccessPolicy, disable DOM node wrapping, and use a template loader that only loads approved files.
遵循关于上传模板的常见问题解答:将?new类解析器设置为ALLOWS_NOTHING_RESOLVER,而不是SAFER_RESOLVER;保持?api禁用(默认设置),使用SimpleObjectWrapper或WhitelistMemberAccessPolicy限制成员访问,禁用DOM节点包装,并使用仅加载批准文件的模板加载器。

User-Supplied Templates / 用户提供的模板

Some products let users write templates by design, such as email builders, content management system themes, and report designers. Treat this as a privileged feature that runs user-written code:

一些产品允许用户通过设计编写模板,例如电子邮件构建器、内容管理系统主题和报告设计器。将此视为一个运行用户编写代码的特权功能:

  • Limit template editing to authorized roles and log template changes for audit.
    中文:限制模板编辑到授权角色,并记录模板更改以供审计。
  • Render with the engine's sandbox or restricted configuration from the table above and keep the engine up to date.
    中文:使用上表中引擎的沙盒或受限配置进行渲染,并保持引擎更新。
  • Register only filters, functions, and globals that are safe to call with any arguments a template author chooses. The sandbox does not limit what your own code does once it is called.
    中文:仅注册对模板作者选择的任何参数都安全的过滤器、函数和全局变量。沙盒不会限制您的代码在被调用后执行的操作。
  • Contain what the sandbox does not. As Twig's documentation explains, a sandbox does not limit CPU or memory use or make the rendered output safe, and Jinja2 and FreeMarker give the same warning about resources. Treat the output as untrusted, and render in an isolated process or container with time and memory limits, no secrets, and restricted network access.
    中文:包含沙箱不包含的内容。正如Twig的文档解释的那样,沙箱不会限制CPU或内存使用,也不会使渲染的输出安全,Jinja2和FreeMarker也提供了相同的资源警告。将输出视为不可信,并在具有时间、内存限制、无秘密和受限网络访问的隔离进程或容器中渲染。

References / 参考