防盗链不能靠解密cookie实现,因cookie仅url编/解码而非加密,且易被篡改;应组合校验合法登录态cookie与referer白名单,二者缺一不可。

防盗链本身不依赖 Cookie 解密,所谓“防盗链 Cookie”通常指用 Cookie 做访问凭证(如登录态)配合 Referer 校验,而非对 Cookie 值做加密解密来实现防盗。直接对 Cookie 解密解析既无标准做法,也容易误入歧途。
为什么不能靠解密 Cookie 实现防盗链
Cookie 是客户端存储、可被篡改的键值对,http.SetCookie 或 c.SetCookie 发送的值默认只做 URL 编码(url.QueryEscape),不是加密;Gin 的 c.Cookie(name) 方法内部会自动调用 url.QueryUnescape 还原,这仅是编/解码,不是加/解密。
- 伪造成本极低:攻击者可手动构造请求头或浏览器控制台修改 Cookie 值
- Referer 更不可信:空、
about:blank、data:、任意伪造域名都可能出现 - 真正需要的是组合校验:比如「有合法登录态 Cookie」+「Referer 主机在白名单内」,二者缺一不可
Referer 白名单校验中间件怎么写
必须把 Referer 解析和白名单比对拆开做,不能依赖 Cookie 内容反推来源合法性。
- 用
r.Header.Get("Referer")获取原始值,注意它可能为空或格式非法 - 用
url.Parse解析后检查u.Host,不要用u.String()或完整 URL 匹配 - 白名单应为纯域名列表(如
[]string{"example.com", "app.example.com"}),避免带协议或路径 - 若部署在 Nginx 后,确认
proxy_set_header Referer $http_referer;已开启,否则该头可能丢失
示例片段:
func RefererWhitelist(whitelist []string) gin.HandlerFunc {
return func(c *gin.Context) {
referer := c.Request.Header.Get("Referer")
if referer == "" {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "missing referer"})
return
}
u, err := url.Parse(referer)
if err != nil || u.Host == "" {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "invalid referer"})
return
}
allowed := false
for _, domain := range whitelist {
if u.Host == domain || strings.HasSuffix(u.Host, "."+domain) {
allowed = true
break
}
}
if !allowed {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "referer not allowed"})
return
}
c.Next()
}
}
Cookie 里存什么才合理
如果要用 Cookie 辅助防盗链,只应存服务端可控、有时效性、带签名的凭证,比如 JWT 或自定义 session ID。
- 避免存明文用户 ID、权限字段——这些应由后端查库或 Redis 验证,而非信任 Cookie 值
- 设置
HttpOnly: true和Secure: true(HTTPS 环境下),防止 XSS 泄露 - 用
SameSite: http.SameSiteStrictMode或http.SameSiteLaxMode降低 CSRF 风险 - 前端 AJAX 请求需显式设置
withCredentials: true,后端跨域响应头必须匹配:Access-Control-Allow-Credentials: true且Access-Control-Allow-Origin不能为*
真正难处理的从来不是“怎么解密”,而是“如何让 Referer + Cookie 的组合判断在 CDN、代理、移动端 WebView 等各种链路中依然稳定有效”。部署前务必实测真实访问路径下的请求头透传情况。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











