flask和django xss漏洞主因是开发者绕过默认转义:误用|safe、render_template_string拼接用户输入、js/属性上下文未用|tojson或|escapejs、前端直接innerhtml渲染未净化内容。

Flask 和 Django 项目里绝大多数 XSS 漏洞,不是框架没防护,而是你亲手绕过了它——比如加了 |safe、用了 render_template_string 拼用户输入、或在 <script></script> 里直接插 {{ user_input }}。
别用 |safe 放行未清洗的用户数据
这是最常踩的坑:|safe 不是“让内容显示出来”的开关,而是“我已确认这段 HTML 绝对干净”的责任声明。一旦加了,Jinja2 或 Django 就彻底跳过转义。
- 错误写法:
{{ user_comment|safe }}(user_comment来自表单或数据库) - 正确做法:
{{ user_comment }}(默认已转义为纯文本) - 真要渲染富文本?必须先用
bleach.clean()白名单过滤:{{ bleach.clean(user_html, tags=['p', 'br', 'strong'], strip=True) | safe }} - 注意:
bleach.clean()必须在模板里调用,或封装成自定义过滤器;不要在视图函数里提前清洗再传入——那样会丢失上下文语义
禁止在 Python 层拼接 HTML 字符串
只要用了 f"<div>{user_input}</div>" 或 str.format() 构造响应体,就等于主动弃守 Jinja2/Django 的所有转义机制。
- 危险示例:
return f"<h2>Hello, {request.args.get('name')}</h2>" - 安全替代:
return render_template("hello.html", name=request.args.get('name')),模板中写{{ name }} - 极少数必须拼 HTML 的场景(如发邮件),先调
html.escape():from html import escape; safe_name = escape(name) -
render_template_string()同样危险:错例render_template_string(f"<p>{user_input}</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill3219" title="python全能编程助手"><img src="https://img.php.cn/upload/skill/000/000/081/178952049933674.jpg" alt="python全能编程助手" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill3219" title="python全能编程助手" class="overflowclass">python全能编程助手</a> <p class="overflowclass">SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、</p> </div> <a rel="nofollow" href="/xiazai/skill3219" title="python全能编程助手" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>");正例render_template_string("<p>{{ msg }}</p>", msg=user_input)
<script></script> 和属性值里不能只靠 {{ }} 默认转义
HTML 转义只防 、<code>>、&,但在 JS 字符串或 HTML 属性里,单引号、反斜杠、=、onerror 等照样能触发执行。
- JS 上下文错误写法:
<script>var name = "{{ user_input }}";</script> - JS 上下文正确写法:
<script>var name = {{ user_input | tojson }};</script>(Flask/Jinja2)或{{ user_input | escapejs }}(Django) - 属性值避免裸插:
@#@#@#@#@#@#@#@#@#@0→ 改用@#@#@#@#@#@#@#@#@#@1 - ID/class 等属性务必加引号并转义:
<div id="{{ user_id | e }}"></div>(e是escape别名) - 绝对禁用
javascript:、data:text/html等伪协议——得从输入源头拦截,后端转义拦不住
前端也要守住最后一道线
后端返回 JSON(如 jsonify({'title': '<script>alert(1)</script>'}))时,Flask 不做任何 HTML 转义——那是模板层的事。前端若直接 innerHTML 拼接,等于把漏洞平移过去。
- 纯文本字段(
username、title)一律用el.textContent = data - 必须渲染 HTML 字段(如文章正文)?禁用
innerHTML,改用DOMPurify.sanitize(html)后再赋值 - Vue 中禁用
v-html,React 中禁用dangerouslySetInnerHTML——除非你已在后端用bleach.clean()做过白名单过滤 - CSP(
Content-Security-Policy)是重要补充,但不能代替输出转义:它拦不住内联事件或已存在的恶意<script></script>
真正难防的从来不是 <script>alert(1)</script> 这种明面上的代码,而是藏在昵称里的 onmouseover=alert(1)、伪装成图片的 data:image/svg+xml,<script></script>、或经过 base64 编码的 payload——防御必须分层:输入过滤 + 输出转义 + CSP + 前端 sanitization,缺一不可。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










