html/template默认能防95% xss,出问题主因是混用text/template或滥用template.html;它按上下文转义但不校验语义,需配合url.url、template.js等类型及bluemonday清洗方可安全处理富文本与动态属性。

只要用对包、插对位置、不乱绕过,html/template 默认就能拦住 95% 的 XSS;出问题基本不是它不行,而是你主动把它关了。
为什么 html/template 还被 XSS 了
绝大多数情况是混用了 text/template,或者在不该信任的地方打了 template.HTML 的补丁。
- 写
import "text/template"却以为自己在用 HTML 安全模板——它根本不会转义,{{.UserInput}}里的<script>alert(1)</script>会直接执行 - 没显式 import
"html/template",却用template.New(""),Go 可能 fallback 到text/template(尤其在未启用 go modules 或路径冲突时) - 把前端富文本编辑器提交的原始 HTML 直接包成
template.HTML(userInput)传进模板——等于给攻击者开了一扇门 - 调用已弃用的
{{.Raw | safeHTML}},这个函数在 Go 1.12+ 不再推荐,且不做任何过滤,只改类型
href、src、onclick 里插用户数据为什么还是危险
html/template 能识别上下文并转义,但它不负责语义判断。比如它知道 href="..." 是 URL 上下文,会转义 " 和 &,但不会拒绝 javascript:alert(1) 或 data:text/html,...。
- 必须加双引号:
href="{{.URL}}",不加引号会让模板无法识别上下文,导致转义失效 -
.URL字段不能是普通字符串,得是url.URL类型,或用template.URL包装:template.URL("https://" + userHost) -
template.URL会自动剔除javascript:、vbscript:、data:等危险协议,只保留http://、https://、//、/ - 绝对不要把用户输入拼进
onclick="{{.JSCode}}"——JS 上下文要用template.JS包装,且最好避免这种写法
怎么安全输出富文本内容
富文本不是“多插几个标签”那么简单,它需要三步闭环:前端限制格式 → 后端白名单清洗 → 显式标注可信。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 别用正则删
<script></script>标签——HTML 结构太复杂,正则根本不可靠,<img src="x" onerror="alert(1)">就能绕过 - 用
bluemonday做白名单过滤:bluemonday.UGCPolicy().Sanitize(userInput),它默认只放行<p></p>、<strong></strong>、<img src alt>等安全标签和属性 - 过滤后的字符串再转成
template.HTML类型:SafeContent: template.HTML(cleaned) - 模板里只写
{{.SafeContent}},不加任何 filter,不调用safeHTML - 注意:
template.HTML是空 struct 别名,零值是空字符串,不能用nil初始化
html.EscapeString 能不能代替 html/template
不能。它只转义 5 个字符:、<code>>、"、'、&,对 javascript:alert(1)、onerror=alert(1)、data:text/html,<script></script> 完全无效。
-
html.EscapeString只适合纯文本插入 HTML 内容区,比如<p>{{.Text}}</p> - 用在属性值里?它不处理协议校验,也不做 URL 编码
- 用在
<script></script>标签内?它不处理 JS 字符串转义,引号和反斜杠会破坏语法 - 手动拼 HTML 字符串再传给模板?
html/template会把它当纯文本转义,得不到预期效果
最易被忽略的点是:安全边界不在模板里,而在 handler 层——所有用户输入的净化、协议校验、类型包装,都得在调用 template.Execute 之前做完。模板本身只是执行器,不是守门员。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










