html/template默认自动转义防xss,无需手动调用转义函数;必须严格使用该包、禁用text/template和safehtml、过滤前置至handler层,属性/js/css插入须用引号及template.url/jss/css。

只要用对 html/template 包、不手动绕过转义、不混用 text/template,就根本不需要“找转义函数”——它默认就做,而且比你手写的更安全。
为什么别在模板里调用 html.EscapeString
这个函数是给非模板场景用的,比如读文件后拼日志、构造邮件正文、生成 JSON 响应体。直接塞进 html/template 里反而会出问题:
- 模板引擎看到的是普通
string,仍会再做一次上下文感知转义,导致双重转义(比如&) - 你手动转义后又传
template.HTML(...),等于白干,还可能漏掉属性上下文里的危险协议 -
html.EscapeString只处理 5 个字符(、<code>>、&、"、'),但html/template在href=""或onclick=""里还会做 URL/JS 层级编码,它更细
template.HTML 不是“转义函数”,是类型契约
它不是让你“把字符串变安全”的工具,而是你向模板声明:“这段 HTML 我已清洗过,你别动”。误用就是开后门:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 用户提交的富文本、API 返回的字段、表单输入值,一律禁止
template.HTML(userInput) - 后台编辑的公告、CMS 管理员录入的 HTML 片段,才考虑用;且必须先过
bluemonday白名单过滤 -
template.HTML是空 struct 别名,零值是空字符串,不能用nil初始化 - 模板里写
{{.SafeContent}}就行,别加| safeHTML(Go 1.12+ 已弃用)或{{printf "%s" .SafeContent}}(无效)
读文件内容必须先 html.EscapeString 再传模板
html/template 不会自动帮你转义 os.ReadFile 读出来的字符串。它只对模板插值做上下文转义,前提是字段是 string 类型:
- 正确链路:
content, _ := os.ReadFile("page.md")→escaped := html.EscapeString(string(content))→data := struct{ Content string }{escaped}→ 模板写{{.Content}} - 如果文件本身含合法 HTML(如 CMS 导出的
article.html),就不能只转义,得先用bluemonday过滤,再转template.HTML(cleaned) - 绝对不要
strings.ReplaceAll(raw, "\n", "<br>")
后再传template.HTML—— 这样会把用户输入的onerror="alert(1)"也原样包进去
href="{{.URL}}" 即使转义了还是危险,怎么办
html/template 知道这是 URL 上下文,会转义 " 和 &,但它不校验协议、不拒绝 javascript:、不验证域名。所以:
- 后端必须把 URL 字段解析成
url.URL类型,或用template.URL包装(不是字符串) - 前端提交时限制协议白名单(只允许
http、https、//),后端再校验一遍 - 禁止拼接:
href="{{.Protocol}}://{{.Host}}/{{.Path}}"—— 模板无法识别这种动态拼接的上下文,转义失效 -
style和onclick同理:用template.CSS和{{.Data | js}}(仅限模板内,且数据需已预处理)
最常被忽略的点:上下文识别依赖语法完整性。缺引号、标签未闭合、属性值没包裹,都会让 html/template 失去判断依据,退化为简单字符串替换——这时候,它就和 text/template 没区别了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










