Go html/template 默认对所有 {{.}} 插值自动进行上下文感知的HTML实体转义以防止XSS,仅该包具备此机制,text/template不转义;显式使用template.HTML会跳过转义,需谨慎处理。

Go html/template 默认如何转义变量
Go 的 html/template 包在渲染时**自动对所有 {{.}} 插值做 HTML 实体转义**,这是它防 XSS 的核心机制。比如字符串 会被渲染成 ,浏览器不再执行脚本。
这种转义发生在模板执行阶段,且基于上下文(context-aware):在 HTML 标签内、属性值中、JS 字符串里、CSS 值中,会使用不同策略转义,避免“绕过式注入”。
关键点:
- 仅
html/template有此行为;text/template完全不转义,直接拼接 - 转义只作用于通过
{{.}}、{{.Field}}、{{template "name" .}}等普通插值方式输出的内容 - 不会转义你手动写死在模板里的 HTML(如 ),那些需开发者自行确保安全
什么时候会跳过转义?危险的
html.Raw和template.HTML如果你显式把数据包装成
template.HTML类型,html/template就认为“这段已安全”,跳过转义——这是唯一常见且易误用的绕过点。立即学习“go语言免费学习笔记(深入)”;
典型错误写法:
func handler(w http.ResponseWriter, r *http.Request) { userContent := r.URL.Query().Get("content") // ❌ 危险:未经校验就转为 template.HTML data := struct{ Content template.HTML }{template.HTML(userContent)} t.Execute(w, data) }此时若传入
?content=,就会触发 XSS。正确做法:
- 除非你完全控制内容来源(如 CMS 后台审核过的富文本),否则不要用
template.HTML - 若必须渲染 HTML 片段,先用
bluemonday或go-sanitize库白名单过滤,再转template.HTML - 永远不要把
url.QueryEscape或html.EscapeString的结果塞给template.HTML——它们不是等价替代
属性值、JS、CSS 中的插值是否同样安全?
是的,
html/template能识别上下文并做对应防护,但前提是**写法规范**:- ✅ 安全写法:、
、- ❌ 危险写法:
—— 因为双引号被闭合,后续可注入" onclick=alert(1)//- ❌ 更糟写法:
—— JS 上下文未被识别,可能被当 HTML 解析推荐替代方案:
- 属性值统一用双引号包裹,并确保变量只出现在引号内(如
href="{{.URL}}") - JS 数据尽量走 JSON 序列化:
var data = {{.JSONSafeData | safeJS}};,配合自定义safeJS函数返回template.JS类型 - 绝对避免在事件处理器属性(
onclick、onload)中直接插值
与第三方模板引擎或前端框架混用时的隐患
如果你在 Go 模板里嵌入了前端框架代码(如 Vue 的
{{ message }}或 React 的{data}),html/template无法识别这些语法,会把它们当普通文本处理,导致双重转义或漏转义。例如:
这里
{{.UnsafeHTML}}会被 Go 模板转义,但 Vue 又会把它当 HTML 渲染——如果原始值含,Go 已转成zuojiankuohaophpcnscriptyoujiankuohaophpcn,Vue 不会再执行,看似“安全”,实则破坏了预期语义;更糟的是,若你提前用template.HTML绕过 Go 转义,Vue 就真会执行恶意脚本。应对原则:
- 前后端模板职责分离:Go 模板只负责骨架和可信数据,动态内容交由前端框架通过 API 获取
- 如必须混用,用
template.JS或template.CSS显式标注类型,并确认前端框架不重复解析 - 禁止在 Go 模板中拼接任何前端框架的表达式语法
最常被忽略的一点:开发者以为用了
html/template就万事大吉,却在中间加了一层字符串拼接、JSON unmarshal 再 marshal、或用fmt.Sprintf拼 HTML 片段——这些操作全都脱离了模板的上下文感知能力,转义失效。 - ❌ 危险写法:
- 除非你完全控制内容来源(如 CMS 后台审核过的富文本),否则不要用










