模板变量默认自动转义,手动拼接用户输入到httpresponse易致xss;应使用render()等函数配合模板{{ user_input }},由django自动转义。

模板变量默认就转义,别手动拼HTML
直接用 HttpResponse 拼接用户输入的字符串,是XSS最常见入口。比如写 HttpResponse(f"<div>{user_input}</div>"),只要 user_input 是 "<script>alert(1)</script>",浏览器就真执行了。
正确做法是交给Django模板系统处理:用 render() 或 render_to_response(),再在模板里写 {{ user_input }}。Django会自动把 变成 <code>、<code>" 变成 ",输出纯文本,不解释为HTML。
- 所有从
views.py传进模板的变量,只要没显式关掉转义,都安全 - 不要在视图里调用
html.escape()再塞进模板——重复转义会导致显示乱码,比如< - 调试时如果看到页面源码里是
<script></script>而不是<script></script>,说明转义生效了
谨慎使用 |safe 过滤器
|safe 的作用是告诉Django:“这段内容我确认过,可以当HTML渲染”。一旦用了它,自动转义就失效,XSS风险立刻回来。
常见误用场景:
- 富文本编辑器返回的HTML(如TinyMCE、CKEditor),未经清洗就直接
{{ content|safe }} - 后台管理界面里,把用户昵称字段标记为
|safe,结果有人昵称设成"<img src="https://img.php.cn/?x-oss-process=image/resize,p_40" alt="如何解决Python Django项目中的XSS跨站脚本攻击漏洞_利用模板自动转义功能">" - 用
mark_safe()包裹数据库读出的字符串,但没做任何过滤
如果必须用 |safe,请先用 django.utils.html.strip_tags() 去掉所有标签,或用 bleach.clean() 白名单过滤(只留 p、strong 等安全标签)。
注意 {% autoescape %} 块级控制
模板里可以用 {% autoescape off %} 关闭整块区域的转义,但它不是“局部豁免”,而是整段失效。很多人以为只对下面一行起作用,其实会影响整个 block 直到配对的 {% endautoescape %}。
容易踩的坑:
- 写成
{% autoescape off %}{{ user_input }}{% endautoescape %},本意是只让这个变量不转义,但实际是整块都不转义 - 嵌套模板中父模板关了转义,子模板继承后也继承了这个状态,容易遗漏
- 用
{% autoescape off %}后又忘了加{% endautoescape %},导致后面所有变量都裸奔
替代方案更稳妥:用 {{ user_input|escape }} 显式转义,或用 {{ user_input|default:"<empty>"|escape }}</empty> 组合过滤器。
开发环境 debug 页面也有XSS风险
Django debug页面(500错误页)在旧版本(如1.11.4及之前)存在CVE-2017-12794漏洞:异常信息里的用户输入未转义,可被注入脚本。虽然生产环境 DEBUG=False 会隐藏debug页,但本地开发时仍可能触发。
应对方式:
- 确保Django升级到1.11.5+、2.0.1+、2.1+等已修复版本
- 开发时避免在异常消息里直接拼接用户可控数据,例如不要写
raise ValueError(f"Invalid input: {request.GET['q']}") - 上线前检查
settings.py中DEBUG必须为False,且ALLOWED_HOSTS配置正确,否则debug页可能被外部访问
真正难防的不是模板层,而是那些绕过模板、直出HTML的路径——比如自定义中间件生成响应、第三方库回调、AJAX返回的HTML片段。这些地方没有自动转义兜底,得靠开发者自己守好每一道门。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











