默认启用的模板自动转义仅防html文本节点xss,对href、onclick等属性上下文无效;必须按上下文选用urlencode、escapejs等过滤器;|safe仅限可控、未读写、经bleach清洗的内容;禁用autoescape极危险;ajax渲染需前端dompurify兜底。

默认启用的模板自动转义能挡住大部分XSS,但只要变量出现在href、onclick、style或script等上下文中,它就完全失效——这不是配置问题,而是HTML解析机制决定的。
为什么{{ user_input }}在
里安全,在属性值里却危险
Django只对HTML文本节点做转义,不分析语义。比如:<a href="%7B%7B%20url%20%7D%7D"></a>中,如果url是javascript:alert(1),浏览器会直接执行;又比如<div class="{{ cls }}">中,若<code>cls是btn onmouseover=alert(1),引号缺失+事件绑定就触发了JS执行。
这类场景下,必须手动使用上下文感知的转义函数:
- URL上下文:用
urlencode过滤器({{ url|urlencode }}),或iriencode处理含中文的URL - JavaScript字符串上下文:前端用
JSON.stringify()包裹,后端别拼<script>var x = "{{ data }}";</script> - CSS类名/样式值:强制加引号,并用
escapejs({{ cls|escapejs }})处理引号和反斜杠 - 内联事件(应避免):绝不用
onclick="{{ handler }}",改用data属性+事件委托
慎用|safe和mark_safe的三个硬性条件
|safe不是“让富文本显示出来”的开关,而是“我已确认这段HTML不含<script></script>、on*事件、javascript:协议”的法律声明。
只有同时满足以下三点,才能用|safe或mark_safe():
- 内容来源完全可控(如后台管理员手工录入的公告HTML,非用户提交)
- 内容未经过任何数据库读写(避免历史脏数据混入)
- 已用
bleach.clean()做过白名单过滤(仅保留<p></p>、<strong></strong>、<ul></ul>等,剥离所有style属性和事件)
常见错误:把TinyMCE编辑器输出的content直接{{ content|safe }}——哪怕编辑器声称“安全”,它也不阻止用户切换源码模式手写<img src="x" onerror="fetch('/api/token')">。
绝对不要禁用autoescape,除非你正在写模板引擎本身
{% autoescape off %}不是“局部关闭转义”的快捷方式,它会让从该标签开始到{% endautoescape %}之间所有{{ }}变量全部裸奔,包括后续include进来的子模板里的变量。
实际项目中,99%的所谓“需要关转义”需求,本质是前端结构没设计好。正确解法是:
- 把需要渲染HTML的部分拆成独立字段(如
rich_intro),在视图里用bleach.clean()处理后再传给模板 - 模板中仍用
{{ rich_intro|safe }},但安全责任明确落在清洗逻辑上,而非模板开关 - 永远不在base.html或通用partial里用
{% autoescape off %}——污染范围不可控
最易被忽略的点:Django的转义保护只覆盖服务端渲染路径。一旦你用AJAX加载JSON再用innerHTML插入,所有模板层防护都归零——这时候必须靠前端DOMPurify.sanitize()或textContent兜底。











