string.Template 不提供安全保证,仅严格隔离变量名与表达式但不防注入;模板须来自可信源,替换值需上下文转义,超出占位符需求应换用 Jinja2 等更合适方案。

直接说结论:string.Template 本身不提供“安全”保证,它只是比 % 格式化和 f-string 更严格地隔离变量名与表达式——但它完全不防注入,只要模板字符串来自不可信输入,就可能被恶意构造出意料外的替换行为。
为什么 string.Template 不等于“安全”?
string.Template 的设计目标是简单、可预测的占位符替换(只认 $name 或 ${name}),不支持表达式求值,所以不会像 eval() 那样执行任意代码。但这不等于防注入:
- 攻击者仍可利用替换逻辑绕过预期结构,比如在 HTML 模板中注入
${user_input},而user_input值为"><script>alert(1)</script> - 若模板本身由用户提交(如配置文件、CMS 模板字段),攻击者可插入任意占位符名,诱导你调用
safe_substitute()时漏掉关键校验 -
substitute()在缺失 key 时会直接抛KeyError,但safe_substitute()会原样保留$xxx,这反而可能暴露内部结构或成为 XSS 载体
如何真正用好 string.Template?
关键是控制输入来源和替换上下文:
- 模板字符串必须来自可信源(硬编码、白名单配置文件),绝不能拼接用户输入
- 替换字典的 key 必须严格限定范围,建议用
dict.fromkeys(whitelist, '')初始化,再逐个赋值 - 对替换值做上下文感知的转义:HTML 场景用
html.escape(),SQL 字段名用白名单校验,路径拼接用os.path.join()而非模板 - 避免在模板里写逻辑分支(比如
${if_admin}),string.Template不支持条件,强行模拟只会让校验变复杂
示例:正确用法
立即学习“Python免费学习笔记(深入)”;
from string import Template
import html
<h1>模板来自代码内建,可信</h1><p>tmpl = Template("<div class="user">Hello, $name!</div>")</p><h1>替换值来自用户,但做了 HTML 转义</h1><p>safe_name = html.escape(user_input)
result = tmpl.safe_substitute(name=safe_name)
什么时候该换别的方案?
当需求超出纯占位符替换时,string.Template 就不是最佳选择:
- 需要条件渲染(如“欢迎管理员”/“欢迎访客”)→ 改用
jinja2并开启 autoescape - 要动态拼接 SQL 查询字段 → 直接用数据库驱动的参数化查询(
cursor.execute("SELECT * FROM users WHERE id = %s", [user_id])),别走模板 - 模板内容需支持用户自定义且带沙箱 →
jinja2.SandboxedEnvironment比自己用Template+ 正则过滤靠谱得多 - 性能敏感且模板极简 →
str.replace()或format_map()可能更快,Template有额外解析开销
真正容易被忽略的点是:安全不在模板引擎本身,而在你如何界定“哪部分可控、哪部分不可控”。string.Template 只帮你守住“不执行代码”这一条线,其余防线得你自己一层层补上。


















