Flask默认Jinja2自动转义仅对{{}}变量生效,可防大部分反射型XSS;但遇|safe、Markup、autoescape false、富文本直出、前端innerHTML等场景即失效,需结合输入过滤、bleach清洗、CSP及前端textContent等分层防护。

Flask默认用Jinja2,但render_template不等于绝对安全
Flask默认启用Jinja2的自动转义(autoescape),对{{ variable }}中的变量内容做HTML实体转义,比如把<script></script>变成<script>。但这只在模板变量插值时生效,一旦你显式写了{{ variable|safe }}、{% autoescape false %},或用了Markup包装,转义就绕过了。
常见踩坑点:
- 从数据库读取富文本(如用户评论)后直接
|safe渲染——这是XSS高发场景 - 前端用
innerHTML = data拼接后端返回的未过滤HTML字符串 - API接口返回JSON,但前端用
v-html(Vue)或dangerouslySetInnerHTML(React)直接渲染字段
什么时候该关掉Jinja2自动转义?关了之后怎么补救
必须关闭自动转义的场景极少,典型的是渲染已审核通过的富文本(如CMS后台编辑器输出)。关掉后不能裸放数据,得靠双重防护:
- 后端清洗:用
bleach库白名单过滤HTML标签,例如bleach.clean(html, tags=['p', 'br', 'strong'], strip=True) - 前端再拦截:即使后端返回了clean过的HTML,前端仍需避免用
innerHTML直接写入不可信上下文;可用textContent替代,或对动态插入的HTML做二次DOMPurify.sanitize() - 永远不要对
request.args、request.form、request.json等原始输入直接|safe
前端JavaScript里怎么安全处理Flask返回的用户数据
Flask后端返回JSON时,默认不会对字符串做HTML转义(那是模板层的事),所以jsonify({'content': '<script>alert(1)</script>'})会原样发过去。前端拿到后如果直接插入DOM,就中招了。
立即学习“Python免费学习笔记(深入)”;
- 服务端返回纯文本字段(如
title、comment)时,前端一律用textContent或innerText赋值,不用innerHTML - 若必须渲染HTML(如文章正文),后端应使用
bleach清洗,并在响应头加X-Content-Type-Options: nosniff防MIME混淆 - 避免在JS模板字符串里拼接用户数据:
el.innerHTML = `<div>${data}</div>`→ 改用el.textContent = data或document.createTextNode(data)
用escape函数手动转义比依赖模板更可控
Jinja2的escape过滤器({{ variable|escape }})和Python内置html.escape()本质一样,但手动调用能让你把转义时机前移到数据组装阶段,而不是等到模板渲染。这对API路由尤其有用——你可以在序列化前统一处理。
- 在
jsonify前对敏感字段调用html.escape():jsonify({'name': html.escape(user_input)}) - 注意
html.escape()默认不转义quot和apos,如需兼容XML/HTML双引号属性,加参数quote=True - 别在已经转义过的字符串上重复调用
escape,会导致这类双重编码
|safe、innerHTML或jsonify裸传,风险就暴露了。


















