bigywater.tech
Django · 84

模板自动转义与 safe

自动转义防住了多少?它防不住什么?这篇不讲原理,直接用一组「攻击字符串」喂给不同的上下文,看输出。

5 个字符
< > & " ' 分别转成 < > & " '
不拦协议
href="{{ url }}" 遇到 javascript:alert(1) 原样输出,要自己校验协议
mark_safe + 拼接
把用户输入拼进 mark_safe 就是 XSS;format_html 才会转义参数

转义与 safe

实测 · 本机执行输出
{{ x }}          → &lt;b&gt;&amp;&quot;&#x27;&lt;/b&gt;
{{ x|safe }}     → <b>&"'</b>
mark_safe 的对象 → <b>&"'</b>
{% autoescape off %}{{ x }}{% endautoescape %} → <b>&"'</b>
对同一个字符串既 safe 又 upper(escape 时机): &lt;B&gt;A&amp;AMP;B&lt;/B&gt;

safe 是给字符串打「已经安全」的标记,不是「把它变安全」。

上下文

实测 · 本机执行输出
href="{{ url }}" → <a href="javascript:alert(1)">x</a>   ← 不会被转义或拦截(需要自己校验协议)
<script>var n = "{{ name }}";</script> → <script>var n = "&quot;;alert(1);//";</script>   ← HTML 转义把 " 变成 &quot;,在 JS 字符串里并不是安全的写法
用 escapejs 过滤器: <script>var n = "\u0022\u003Balert(1)\u003B//";</script>
用 json_script(推荐): <script id="d" type="application/json">{"name": "\";alert(1);//", "x": "\u003C/script\u003E\u003Cb\u003E"}</script>

自动转义是针对 HTML 文本上下文的。属性、URL、<script>、CSS 各有各的转义规则:URL 要校验协议,JS 要用 json_script(或 escapejs)。

属性引号

实测 · 本机执行输出
<input value={{ v }}> → <input value=x onmouseover=alert(1)>   ← 属性没加引号,空格仍能截断
<input value='{{ v2 }}'> → <input value='&#x27; onclick=&#x27;x'>

Django 转义了单引号(&#x27;),所以带引号的属性是安全的;没引号的属性靠空格就能破坏。

在 Python 里拼 HTML

实测 · 本机执行输出
format_html("<p>{}</p>", u) → <p>&lt;img src=x onerror=alert(1)&gt;</p>
mark_safe("<p>" + u + "</p>") → <p><img src=x onerror=alert(1)></p>   ← 用户输入被原样标成安全
escape(u) → &lt;img src=x onerror=alert(1)&gt;

凡是要产出 HTML 的 Python 代码用 format_html、format_html_join,不要手工拼字符串再 mark_safe。

几个易混的过滤器

实测 · 本机执行输出
模板里的字面量 {{ "<i>lit</i>" }} → <i>lit</i>
join 过滤器对列表元素转义: &lt;a&gt;, b
linebreaksbr 对内容转义、只把换行变成 <br>: &lt;x&gt;<br>line2
urlize 转链接并转义: see <a href="http://a.com/?q=" rel="nofollow">http://a.com/?q=</a>&lt;x&gt;

这些是常见过滤器在自动转义下的行为。

分步演示:转义与上下文

自动转义
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。用 engines["django"].from_string 渲染;攻击字符串仅用于测试转义输出,不涉及真实站点
    • 没有验证:浏览器里实际执行效果(这里只看输出的 HTML 文本)、CSP 对内联脚本的影响、CSS 上下文、Jinja2 后端的转义规则、custom 过滤器返回 safe 的规则(第 85 篇)
    京ICP备2026067502号-1京公网安备11010502063487号