Markup 到底是什么?|safe 做了什么?为什么用 f-string 包一层 Markup 会出 XSS?什么写法才是安全地拼接 HTML?这篇用同一个恶意输入把各种写法对比一遍。
Markup("<b>x</b>"): Markup('<b>x</b>') | 是 str: True | 有 __html__: True
escape("<b>&\"'"): Markup('<b>&"'') | 类型: Markup
对 Markup 再 escape 不会双重转义: Markup('<b>') | escape(Markup("&")): Markup('&')
escape(None): Markup('None') | escape(5): Markup('5') | escape(对象有 __html__): Markup('<i>已是 HTML</i>')上一篇:自动转义与 XSS 防护;下一篇:Jinja2 宏。过滤器返回 HTML 的写法也见自定义 Template Filter。
Markup("<b>") + u → Markup('<b><script>alert(1)</script>')
u + Markup("<b>") → Markup('<script>alert(1)</script><b>')
Markup("<b>%s</b>") % u → Markup('<b><script>alert(1)</script></b>')
Markup("<b>{}</b>").format(u) → Markup('<b><script>alert(1)</script></b>')
Markup(f"<b>{u}</b>")(错误写法,先格式化再标记安全)→ Markup('<b><script>alert(1)</script></b>')
"<b>%s</b>" % u 再 Markup(...) → Markup('<b><script>alert(1)</script></b>')
Markup.join: Markup('<a><br>b&c') | Markup 拼接 Markup: Markup('<i><b>')
切片、upper、replace 的结果仍然是 Markup: Markup Markup Markup | replace 里的新文本被转义: Markup('<b><y></b>')x → <b>A &amp; B</b> x|safe → <b>A & B</b> x|e → <b>A &amp; B</b> x|escape → <b>A &amp; B</b> x|forceescape → <b>A &amp; B</b> x|striptags → A & B x|string → <b>A &amp; B</b> x|upper → <B>A &AMP; B</B> x|safe|upper → <B>A & B</B> x|upper|safe → <B>A & B</B>
{{ w }} → <button><保存></button> | {{ w|string }} → Widget(<保存>) | {{ [w]|join(",") }} → <button><保存></button> Markup(f"<a title=\"{u}\">") → 危险 <a title=""><img src=x onerror=alert(1)>">
Markup("<a title=\"{}\">").format(u) → 安全 <a title=""><img src=x onerror=alert(1)>">
Markup("<a title=\"%s\">") % u → 安全 <a title=""><img src=x onerror=alert(1)>">
Markup("<a title=\"") + u + Markup("\">") → 安全 <a title=""><img src=x onerror=alert(1)>">
"<a title=\"" + escape(u) + "\">" → 安全 <a title=""><img src=x onerror=alert(1)>"> Markup("<p>a & <b>b</b></p>").striptags() → 'a & b' | Markup("<b>").unescape() → '<b>'