referer验证中间件不能只靠req.referer(),因其可能为空或被伪造;须用req.header.get("referer")获取原始值,解析后校验scheme和host,并预解析白名单url以提升安全性和性能。

Referer验证中间件为什么不能只靠 req.Referer()
直接调用 req.Referer() 获取的值可能为空(比如用户手动输入 URL、HTTPS 页面跳转到 HTTP、浏览器隐私模式),也可能被客户端伪造。单纯比对字符串容易绕过,必须结合协议、主机名、路径前缀等维度判断,且要提前处理空值和异常格式。
实操建议:
- 始终用
req.Header.Get("Referer")替代req.Referer(),避免 Go 标准库自动忽略空 Referer 的隐式行为 - 用
url.Parse()解析后校验u.Scheme和u.Host,不要用字符串strings.Contains()匹配完整 Referer 字符串 - 若允许多个来源,把白名单存为
[]*url.URL(预先解析好),避免每次请求重复解析
如何写一个安全的 Referer 中间件函数
Echo 中间件是 echo.MiddlewareFunc 类型,需返回 echo.HandlerFunc。关键点在于:拦截非白名单请求时,应明确返回 403 而非重定向或静默放行。
示例代码片段(注意错误处理和边界):
func RefererMiddleware(allowed []*url.URL) echo.MiddlewareFunc {
return func(next echo.Handler) echo.Handler {
return echo.HandlerFunc(func(c echo.Context) error {
referer := c.Request().Header.Get("Referer")
if referer == "" {
return echo.NewHTTPError(http.StatusForbidden, "missing Referer")
}
u, err := url.Parse(referer)
if err != nil || u.Scheme == "" || u.Host == "" {
return echo.NewHTTPError(http.StatusForbidden, "invalid Referer format")
}
allowedHost := false
for _, a := range allowed {
if u.Scheme == a.Scheme && u.Host == a.Host {
allowedHost = true
break
}
}
if !allowedHost {
return echo.NewHTTPError(http.StatusForbidden, "Referer not allowed")
}
return next.ServeHTTP(c.Response(), c.Request())
})
}
}
使用时传入预解析的白名单:
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
allowed := []*url.URL{
{Scheme: "https", Host: "example.com"},
{Scheme: "https", Host: "app.example.com"},
}
e.Use(RefererMiddleware(allowed))
为什么不能在 POST 接口里忽略 Referer 验证
很多人误以为“API 接口不用防 CSRF 就不用验 Referer”,但 Referer 验证本质是服务端主动约束调用方来源,和 CSRF Token 是不同层级的防护。比如你提供一个上传接口 POST /api/upload,如果不限制 Referer,攻击者可诱导用户在恶意页面发起表单提交,即使没 Token 也能触发上传逻辑(尤其是未鉴权或弱鉴权接口)。
注意事项:
- GET 接口通常不敏感,但若含敏感参数(如
?token=xxx),也应验证 Referer 防止泄露 - 若接口明确允许 CORS 调用,Referer 验证与
Access-Control-Allow-Origin无冲突,但需注意浏览器同源策略下 CORS 请求的 Referer 可能被浏览器截断(如从 HTTPS 到 HTTP) - Webhook 回调类接口必须关闭 Referer 验证——因为调用方不可控,Referer 可能为空或不符合预期
调试时 Referer 总是空?检查这几个地方
本地开发或测试时经常发现 req.Header.Get("Referer") 返回空字符串,这不是中间件写错了,而是请求本身就没带。
常见原因:
- 用 curl 或 Postman 测试时没手动加
-H "Referer: https://example.com" - 前端用
fetch()且redirect: "manual"或未设置referrerPolicy,导致浏览器不发送 Referer - Nginx 或其他反向代理默认剥离了
Referer头(检查是否有proxy_set_header Referer "";这类配置) - Chrome 扩展(如某些广告屏蔽工具)或企业网络策略主动清除了 Referer
验证方式:在中间件开头加日志,打印原始 header:c.Request().Header,确认字段是否存在而非只看 Referer() 方法返回值。










