{{ var }} 仅对 HTML body 安全,因 Django 自动转义只处理 <、>、&、"、',不识别属性、script、style、URL 等上下文语义,故无法防御对应 XSS。

默认 {{ var }} 是安全的,但仅限 HTML body;一旦进到属性值、script、style 或 URL 上下文,就立刻失效。
为什么 {{ var }} 不能防住所有 XSS 场景
Django 模板对 {{ var }} 的自动转义只做一件事:把 、<code>>、&、"、' 替换成 HTML 实体。它假设你正在往 HTML 文档主体里插内容。
但浏览器解析 HTML 时,会按上下文决定字符含义。比如:
-
<div class="{{ css_class }}">—— 若css_class = 'x" onmouseover="alert(1)',引号提前闭合,JS 就执行了 -
<script>var msg = "{{ data }}";</script>—— 单引号或\不被转义,容易逃逸字符串 -
<a href="{{ url }}">—— 若url = 'javascript:alert(1)',点击即触发
这些地方,模板引擎不识别语义,也不会主动加引号或过滤协议。它只负责“body 内容转义”,不是万能净化器。
立即学习“Python免费学习笔记(深入)”;
哪些地方必须手动用 escape 或上下文专用过滤器
遇到非 body 渲染场景,得靠显式过滤。Django 提供了几个内置过滤器,比自己拼 html.escape() 更可靠:
- 属性值(带引号):
{{ css_class|escape }}或更稳妥的{{ css_class|force_escape }}(避免 double-escape) - URL 上下文:
{{ user_url|urlencode }}—— 注意它只编码参数,不校验协议;若需限制协议,得用urlize或自定义过滤器 -
script内部字符串:{{ js_data|json_script:"my-data" }}(推荐)或{{ js_data|escapejs }},后者会转义引号和反斜杠 - 动态 CSS:
{{ color|escape }}+ 确保属性值始终用引号包裹,如color="{{ color|escape }}"
别用 |safe 或 mark_safe() 去“修复”这类问题——那是把风险直接暴露出来。
什么时候能放心用 mark_safe()
mark_safe() 不是“让 HTML 显示出来”的开关,而是“我已人工确认这段内容不含任何危险片段”的法律声明。
- 只用于完全静态、硬编码的内容,比如
mark_safe('<i class="icon-user"></i>') - 富文本字段(如 TinyMCE 输出)绝不能直接
mark_safe(),必须先过bleach.clean()过滤标签和属性 - 从数据库读出的历史 HTML,要清洗后再标记,不能无脑信任旧数据
- 拼接 URL 或 JS 字符串时,哪怕只有一小段来自用户,整条字符串都不能
mark_safe()
一个常见误判是:“我用了 TinyMCE,它自带过滤,所以 safe 没问题”。错——TinyMCE 配置可能被绕过,且它不处理 onerror= 这类事件属性。
真正该禁用的不是转义,而是 {% autoescape off %} 和 |safe
这两个操作等价于拔掉保险丝。它们在模板里出现的位置,就是 XSS 最可能爆发的地方。
- 全局搜索项目里所有
|safe,逐个确认来源:是静态图标?还是用户头像 alt 文本?后者必须删掉|safe改用|escape - 检查是否有
{% autoescape off %}块包裹了用户输入变量;如有,要么删掉,要么把变量提前用|escape处理 -
render_template_string()是高危函数,它绕过了整个模板层的安全机制;禁止将用户输入传给它
最隐蔽的风险点往往不在视图逻辑里,而在某个被遗忘的模板片段中——那里一行 |safe 就足以让整页失守。


















