string.Template 更安全,因其仅支持 $name 形式变量替换,不执行表达式、不调用方法、不访问属性,恶意输入如 "${__import__('os').system('rm -rf /')}" 会被原样保留或报 KeyError,杜绝代码注入。

直接用 string.Template 替代 .format() 或 f-string 做模板渲染,能有效防止代码注入——前提是只渲染用户可控的字符串字段,且不拼接任意 Python 表达式。
为什么 string.Template 更安全?
它只支持最简变量替换($name 或 ${name}),不执行任何表达式、不调用方法、不访问属性。哪怕传入恶意字符串如 "${__import__('os').system('rm -rf /')}",Template.safe_substitute() 会原样保留,Template.substitute() 则直接抛出 KeyError,不会执行。
对比:"{a.__class__}".format(a=1) 可触发属性访问;f-string 中 f"{os.system('ls')}" 直接执行——这些在 Template 里根本不存在。
如何正确使用 substitute() 和 safe_substitute()
两者核心区别在于缺失键的处理方式:
立即学习“Python免费学习笔记(深入)”;
-
substitute():遇到未提供的占位符(如模板含$user但字典没'user')立即抛出KeyError -
safe_substitute():缺失键时保留原始占位符(如$user不变),适合不确定所有变量是否都存在的场景
示例:
from string import Template
t = Template("Hello, $name! You have $count messages.")
# 缺少 count → KeyError
# t.substitute(name="Alice")
# 安全模式 → 输出 "Hello, Alice! You have $count messages."
t.safe_substitute(name="Alice")常见误用:嵌套、转义与非法字符
Template 对语法非常严格,稍不注意就报错:
- 双美元符号
$$才表示字面量$,写成$单独出现会报ValueError: Invalid placeholder - 不允许
${name.attr}或${name[0]},只认纯标识符(字母/数字/下划线,不能以数字开头) - 若变量名含连字符或空格(如
"user-name"),必须用${user-name}形式,但需确保传入字典的 key 是字符串"user-name",而非user-name(这会是语法错误) - 模板中出现未闭合的
${(如${unclosed)也会触发ValueError
和 Jinja2 / format 的边界在哪?
string.Template 不是万能替代品:
- 没有条件、循环、过滤器——需要这些功能,必须换 Jinja2 等模板引擎
- 不支持类型转换(如
$price:.2f),数值需提前格式化好再传入 - 性能略优于
.format()(尤其大量简单替换时),但差距微小,别为这点优化牺牲可读性 - 真正价值在于“防御性设计”:当你明确只要填空、绝不允许逻辑注入,且输入来源不可信(如配置文件、用户提交的邮件模板),它就是最轻量可靠的选项
安全不是靠函数名带 “safe”,而是靠它压根不解析表达式——这个限制本身,就是它的保护机制。


















