html/template自动转义仅对HTML文本、双引号属性值、JS字符串、CSS样式四类上下文有效;脱离即失效,需配合bluemonday预过滤、协议校验及CSP等多层防护。

html/template 自动转义不是万能盾,它只在你用对上下文的时候才真正起作用。
为什么 html.EscapeString 不能当 XSS 防御主力
它只转义 、<code>>、"、'、& 这 5 个字符,对 javascript:、onerror=、data:text/html 之类完全没反应。比如用户填了 javascript:alert(1),你塞进 <a href="%7B%7B.URL%7D%7D"></a>,html.EscapeString 不会动它分毫——浏览器照常执行。
- 适用场景仅限:纯文本插入 HTML 内容区,例如
<p>{{.Text}}</p> - 不适用于属性值(
href、src)、<script></script>内部、style或事件处理器 - 别把它和“过滤”混淆——它只是编码,不是解析 HTML 结构
什么时候 html/template 会悄悄失效
它默认只对四种上下文做转义:HTML 文本节点、双引号包围的属性值、JS 字符串字面量、CSS 内联样式。一旦脱离这四类,就交由你兜底。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
-
{{.URL | printf "%s"}}:绕过类型系统,printf后直接拼进模板,自动转义失效 -
{{.Content | safeHTML}}或template.HTML(userInput):等于主动关掉防护 -
<img src="%7B%7B.Src%7D%7D" onclick="{{.Handler}}" alt="Golang如何防止XSS攻击_Golang防XSS教程【推荐】" >:.Src和.Handler都不在安全上下文中,不校验协议也不阻止onerror -
<script>var name = "{{.Name}}";</script>:即使.Name经过html/template渲染,输出仍是字符串,若含"和\,仍可逃逸
富文本必须用 bluemonday,别信正则清洗
正则删 on\w+= 或 javascript: 是典型幻觉防御——onmousemove=alert(1)//、JaVaScRiPt:、javascript: 全都能绕过。正则不是 HTML 解析器。
- 安装:
go get github.com/microcosm-cc/bluemonday - 用
bluemonday.UGCPolicy(),不是StrictPolicy(后者会干掉所有格式) - 过滤必须在进入模板前完成:
clean := p.Sanitize(input),再传给html/template渲染 - 绝不要把
bluemonday结果包成template.HTML就完事——它只是净化 HTML,不是免检通行证
HTTP 头和 CSP 是补位,不是替代
Content-Security-Policy: default-src 'self' 能拦住大部分内联脚本和外域资源加载,但它拦不住已注入页面的 DOM XSS,也拦不住服务端拼错上下文导致的执行。
- 必须设
X-Content-Type-Options: nosniff,防 MIME 嗅探误解析 JSON 或 TXT 为 HTML - 所有跳转类属性(
href、src、formaction)在进模板前,要手动校验协议白名单:strings.HasPrefix(url, "https://") || strings.HasPrefix(url, "/") - CSP 的
script-src 'unsafe-inline'一开,前面所有努力基本归零
最危险的不是没过滤,而是你以为 html/template 已经替你拦住了所有东西,结果在某个 onclick 属性里、某次 template.HTML 强转里、某段没走 bluemonday 的富文本里,悄悄漏了一条 onload=eval(atob('...'))。

















