html/template默认可防绝大多数XSS,因其上下文感知自动转义;而html.EscapeString仅转义5个字符且仅适用于纯文本插入,无法防护属性、URL、JS等上下文中的XSS。

html/template 默认就能防住绝大多数 XSS,前提是别主动绕过它——比如用 text/template、手动拼 HTML、或滥用 template.HTML。
为什么 html.EscapeString 不能当 XSS 防护主力
html.EscapeString 只转义 、<code>>、"、'、& 这 5 个字符,对 javascript:alert(1)、onerror=alert(1)、data:text/html,<script></script> 完全无效。
它只适用于纯文本插入 HTML 内容区的场景,比如:<p>{{.Text}}</p>。
一旦用错位置,风险立刻暴露:
- 塞进
href或src属性?必须额外校验协议(拒绝javascript:、data:、vbscript:) - 拼进
<script></script>内部?绝对禁止字符串拼接;应改用json.Marshal输出到data-属性再读取 - 用在
onclick="..."里?漏引号或未 JSON 编码,可能直接导致语法错误 + 绕过
用错模板引擎:text/template 是静默炸弹
text/template 不做任何转义,用户输入什么就原样输出什么。
常见错误写法:
-
import "text/template"+template.New("").Parse(...) - 手动拼接 HTML 字符串:
fmt.Fprintf(w, "<div>"+userInput+"</div>") - 在 handler 中用
strings.ReplaceAll“消毒”后再塞给模板——这反而破坏了html/template的上下文感知转义逻辑
正确做法只有一条:
- 明确导入
"html/template" - 所有动态内容统一走
{{.Field}}插入,让模板自己判断该在哪转义、怎么转义
富文本怎么办:别信正则,用 bluemonday
用户能发带格式的评论或文章?不能靠 template.HTML 伪装安全,必须先过滤 DOM 树。
正则清洗是陷阱:
- onmousemove=alert(1)//、JaVaScRiPt:、javascript%3A 全都能逃逸
- 正则不是 HTML 解析器,处理不了嵌套、编码、大小写混用、注释干扰
实战建议:
立即学习“go语言免费学习笔记(深入)”;
- 安装:
go get github.com/microcosm-cc/bluemonday - 用
bluemonday.UGCPolicy()(不是StrictPolicy,后者会干掉<strong></strong>、<ul></ul>等常用标签) - 过滤后的内容再传给模板:
{{.CleanHTML | safeHTML}},且确保变量类型是template.HTML - 注意:过滤必须在 handler 层完成,不能丢给模板做判断
CSP 和响应头只是补丁,不是防护核心
X-XSS-Protection 已被 Chrome 78+、Firefox 76+、Edge 80+ 全面弃用,Safari 从未支持,删掉这类代码。
真正该配的是 Content-Security-Policy,例如:default-src 'self'; script-src 'self' 'unsafe-inline'。
但记住:CSP 发生在浏览器端,它拦不住已成功注入并渲染的恶意 HTML;它只能阻止后续执行。
所以最易被忽略的一点是:**所有防护都建立在「输出时走 html/template 渲染」这个前提上**。哪怕用了 bluemonday 和 CSP,如果某处裸调 fmt.Fprintf 或误用 text/template,整套防线就从根上断了。


















