gin 不自动转义 html 输出,xss 防护需手动干预:html/template 仅在 {{.xxx}} 渲染时转义,绕过(如 safehtml)即危险;json 接口风险由前端 dom 插入方式决定。

Gin 本身不自动转义 HTML 输出,直接渲染用户输入内容会触发 XSS,必须手动干预。
html/template 自动转义只在 HTML 渲染时生效
很多人误以为只要用了 Gin 的 c.HTML() 就安全了——其实不然。Gin 底层调用的是 Go 标准库 html/template,它确实会对 {{.xxx}} 中的变量做 HTML 实体转义,但前提是:你没显式绕过它。
- ✅ 安全写法:
c.HTML(200, "page.tmpl", gin.H{"content": "<script>alert(1)</script>"})→ 模板里写{{.content}},输出为纯文本 - ❌ 危险写法:
{{.content | safeHTML}}或{{template "unsafe" .}}→ 绕过转义,直接插入 DOM - ⚠️ 注意:
gin.H是 map,不会自动过滤;html/template的转义只发生在模板渲染阶段,不是在gin.H构造时
后端预处理需用 template.HTMLEscapeString 而非 strings.ReplaceAll
如果业务逻辑中需要拼接或组合 HTML 片段(比如富文本摘要截断),不能靠简单替换 、<code>>,因为 XSS 向量太多(如 onerror=alert(1)、javascript:alert() 等)。
- ✅ 正确做法:导入
"html/template",对原始输入调用template.HTMLEscapeString(userInput) - ❌ 错误做法:自己写正则或字符串替换,漏掉事件属性、URL 协议、Unicode 编码绕过等场景
- ⚠️ 注意:该函数只做 HTML 字符实体编码(
→ <code><),不清理标签或属性,所以它适合「纯文本展示」,不适合「富文本渲染」
API 接口返回 JSON 时,XSS 风险转移到前端
Gin 的 c.JSON() 不做任何 HTML 处理,它只是序列化结构体为 JSON。此时 XSS 是否发生,完全取决于前端如何使用这些数据。
- ✅ 安全前端用法:Vue/React 使用
v-text或{children},而非v-html或dangerouslySetInnerHTML - ❌ 危险前端用法:JS 拼接 innerHTML:
el.innerHTML = data.content—— 即使后端返回的是 JSON,照样执行脚本 - ⚠️ 关键点:Gin 无法替前端承担 DOM 插入责任;若必须返回可渲染 HTML(如邮件模板预览),应在 JSON 字段里明确标注用途,并加注释提醒前端校验
真正容易被忽略的是:模板里用 {{index .items 0}} 或 {{.User.Name}} 这类嵌套访问时,如果 .User.Name 来自数据库且未清洗过,依然会触发 XSS —— 转义只看最终输出值,不管它从哪来。











