gin本身不带xss清洗器,防xss必须用bluemonday白名单过滤富文本,禁用模板中safehtml;html.escapestring仅转义5个字符,无法防御属性、url、js等上下文中的xss绕过。

直接上结论:Gin 本身不带 XSS 清洗器,html/template 的自动转义只覆盖「纯文本插入」场景,真要防住富文本里的 <img src="https://img.php.cn/?x-oss-process=image/resize,p_40" alt="Golang中使用Gin框架配置严格的跨站脚本注入防御清洗器"> 这类攻击,必须用 bluemonday 做白名单过滤,且不能在模板里用 {{.Raw | safeHTML}} 绕过。
为什么 html.EscapeString 不够用
它只转义 、<code>>、"、'、& 这 5 个字符。但 XSS 可以绕过它:
- 拼进
href="javascript:..."或onerror="..."属性里,转义后照样执行 - 用户输入
data:text/html,<script>alert(1)</script>,EscapeString完全不碰 - 大小写混写、URL 编码、空格绕过(如
onmousemove=alert(1)//)都逃得掉
所以它只适合:<p>{{.Content}}</p> 这种纯文本上下文,其他位置必须按上下文单独处理。
bluemonday 白名单过滤怎么配
别写正则,别自己定义“允许哪些标签”,直接用现成策略:
- 普通用户评论/简介 →
bluemonday.UGCPolicy()(允许a、strong、em等,禁掉所有事件属性和危险协议) - 后台管理员富文本 →
bluemonday.StrictPolicy()(更保守,连style都默认删) - 自定义策略时,用
AllowAttrs("href").OnElements("a")显式声明,而不是黑名单式删除
示例:
import "github.com/microcosm-cc/bluemonday"
func sanitizeInput(dirty string) string {
policy := bluemonday.UGCPolicy()
return policy.Sanitize(dirty)
}
// 在 handler 中调用,不是在模板里
c.HTML(http.StatusOK, "page.html", gin.H{"Content": sanitizeInput(userInput)})
模板里 template.HTML 的坑在哪
很多人图省事,在 handler 里直接 template.HTML(userInput) 然后传给模板,等于把过滤逻辑甩给前端——只要上游漏掉一个 onload,XSS 就进来了。
-
template.HTML是信任信号,不是过滤动作 - 一旦用了它,就代表你已确认内容“绝对干净”,否则就是主动关掉防护
- 所有
safeHTMLfilter、{{.Raw | safeHTML}}都该删掉,过滤必须前置到 handler 或中间件层
正确链路是:接收 → bluemonday.Sanitize() → 存库/传参 → 模板用 {{.Content}}(走默认转义)输出。
CSP 头是最后一道保险
即使 HTML 被绕过,CSP 能阻止脚本执行。Gin 中加头很简单:
r.Use(func(c *gin.Context) {
c.Header("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data:")
c.Next()
})
注意点:
-
'unsafe-inline'和'unsafe-eval'尽量去掉,改用nonce或hash方式加载内联脚本 -
img-src加data:是为了支持 base64 图片,但会放开部分 XSS 向量,如有严格要求可删 - 配合
X-Content-Type-Options: nosniff防止 MIME 类型混淆
真正难的不是加哪几行代码,而是坚持把过滤逻辑从模板层提到 handler 层,且每次新增富文本字段都重新走一遍 bluemonday 策略评审——没人会为一个 bio 字段写安全方案,但攻击就藏在这种“小地方”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











