referer校验必须解析url后精确匹配host,禁用硬字符串匹配;空referer属正常浏览器行为;中间件校验失败需abortwithstatus后立即return;referer仅为辅助过滤手段,不可替代强认证。

Referer校验为什么不能用strings.Contains硬匹配
直接写strings.Contains(r.Header.Get("Referer"), "example.com")会把合法请求拦掉,也会被轻易绕过。比如浏览器从 HTTPS 页面跳到 HTTP 接口时,Referer可能为空;用户从sub.example.com发起请求,Host是sub.example.com,但硬匹配"example.com"会误判为合法——而"evil.com.example.com"反而能通过。
必须先解析再提取:url.Parse()后取u.Scheme和u.Host,去掉端口(非默认端口才保留)、路径、查询参数;白名单只存纯Host字符串,如"app.example.com",不带协议、斜杠或通配符。
- 空
Referer不是异常,是浏览器策略行为(如 HTTPS→HTTP、Referrer-Policy 设置为no-referrer-when-downgrade) -
r.Referer()是方法,不是字段,别写成r.Referer导致编译错误 - 子域名必须精确匹配,不推荐
strings.HasSuffix(host, whiteItem),优先用host == whiteItem
Gin中间件里做Referer白名单要显式return
Gin中间件中调用c.Abort()或c.AbortWithStatusJSON()只是中断当前中间件链,不会自动跳出函数体。漏掉return会导致后续代码继续执行,甚至走到业务 handler 里——相当于校验形同虚设。
正确写法是:校验失败后立即c.AbortWithStatus(http.StatusForbidden),然后紧跟return。
- 别只写
c.Abort()就结束,后面没return等于没拦住 - 用
c.Request.Referer()获取值,它已做基础空值处理,比c.Request.Header.Get("Referer")更稳妥 - 解析失败(
url.Parse报错)或u.Host == ""都应视为非法,统一返回403
白名单配置要区分场景,不能全局套用
/api/路径下启用 Referer 校验风险极高:单页应用前端路由跳转不刷新Referer;小程序、App、桌面客户端根本不会发这个头;HTTPS 页面加载 HTTP 资源时浏览器会清空它。真要用,只建议限定在/admin/这类内部 Web 管理接口,且确认所有调用方都是同域页面。
静态资源(图片、JS、CSS)相对安全,Nginx 层就能做,Gin 中不必重复校验。
- 对
/static/或/assets/等路径做 Referer 校验意义不大,应交给 Nginx 的valid_referers指令 - 若必须在 Gin 中校验 API,白名单至少包含前端部署域名和本地开发地址(如
"localhost:3000") - 上线前务必开日志记录
$invalid_referer和$http_referer,观察真实流量构成,避免误杀
Referer只是辅助手段,不能替代真正认证
Referer 可被任意伪造:curl -H "Referer: https://trusted.com" ...就能绕过。它唯一价值是过滤掉大量无脑爬虫、盗链脚本和低级恶意调用。合规要求(如等保、GDPR)从不把 Referer 当作独立防护措施。
生产环境必须叠加签名验证、短期 Token、IP 白名单或 OAuth2.0 等强认证机制。Referer 校验只放在最外层,作为第一道轻量过滤网。
- 别在
Content-Security-Policy里依赖referrer指令做权限控制 - 配合
Referrer-Policy: no-referrer-when-downgrade响应头,减少敏感路径泄露 - 如果接口需携带凭证(
credentials: true),Referer 校验失败时更要防止凭证被滥用
return——这两处出问题,整个校验逻辑就失效了。











