必须显式配置SetTrustedProxies,否则X-Forwarded-*头不可信;Gin默认仅信任127.0.0.1,线上经Nginx/SLB/CDN后ClientIP返回内网地址,不配置将导致IP伪造绕过限制。

必须显式配置 SetTrustedProxies,否则所有 X-Forwarded-* 头都不可信
Gin 默认只信任 127.0.0.1,线上走 Nginx/SLB/CDN 后,c.ClientIP() 返回的全是内网地址(如 10.0.1.5),根本不是真实出口 IP。不调用 engine.SetTrustedProxies,就等于把伪造头当真——攻击者发个 X-Forwarded-For: 1.2.3.4, 127.0.0.1 就能绕过所有基于 IP 的限制。
实操要点:
- 在
gin.Default()或gin.New()之后立即调用,不能晚于路由注册 - 网段必须精确匹配你实际使用的代理:阿里云 SLB 是
"100.64.0.0/10",腾讯云 CLB 是"100.64.0.0/11",Cloudflare Enterprise 回源段需查官方文档,别硬套/16 - IPv6 网段也要加,比如 Cloudflare 的
"2a06:98c0::/29",否则 IPv6 请求会 fallback 到不可信的RemoteAddr - 传空切片
[]string{}表示完全不信任任何代理,此时c.ClientIP()退化为c.Request.RemoteAddr解析结果
X-Real-IP 只适合单层代理,多层时必须用 X-Forwarded-For 并取最左可信 IP
X-Real-IP 是 Nginx 直连 Go 服务时最干净的选择,但一旦中间有 CDN 或二级 Nginx,它就变成上一级代理的 IP,不再反映客户端真实出口。而 X-Forwarded-For 是链式结构(如 "203.208.60.1, 10.1.1.1, 10.2.2.2"),最左才是原始客户端 IP —— 前提是第一层代理严格重置了该头,且你只取第一个、并确认该请求来源 IP 在可信网段内。
关键动作:
- Nginx 最外层必须禁用
$proxy_add_x_forwarded_for,改用proxy_set_header X-Forwarded-For $remote_addr强制覆盖 - 后端 Gin 不要直接
strings.Split(c.GetHeader("X-Forwarded-For"), ",")[0]—— 必须先做net.ParseIP()校验,再判断是否为全局单播地址(ip.IsGlobalUnicast()),跳过127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16等私有网段 - IPv6 地址带方括号(如
[2001:db8::1]),提取前得先strings.Trim(ipStr, "[]")
中间件里别直接信 c.ClientIP(),白名单逻辑必须自己解析头 + 校验来源
很多人写 IP 白名单中间件时直接用 c.ClientIP(),却忘了这个方法的行为依赖 engine.ForwardedByClientIP 和 SetTrustedProxies 是否已正确配置。更危险的是,它对 X-Forwarded-For 只取第一个值、不做合法性校验,攻击者塞个 X-Forwarded-For: script>alert(1) 都可能让 strings.Split panic。
安全做法是绕过 c.ClientIP(),手动控制解析流程:
- 先检查
c.Request.RemoteAddr的来源 IP 是否在你配置的可信代理网段内(用net.ParseIP+net.Contains);不在则直接拒绝,不看任何头 - 再读
c.GetHeader("X-Real-IP")(单层)或c.GetHeader("X-Forwarded-For")(多层),按前述规则提取并校验 - 黑名单优先于白名单:先查是否在黑名单,命中则
c.AbortWithStatus(403);再查是否在白名单,不命中也拒绝 - 豁免健康检查路径(如
/healthz),避免探针被误拦
别忽略 Nginx 配置和 Gin 设置的协同关系
光改 Gin 没用。如果 Nginx 没配 proxy_set_header X-Real-IP $remote_addr,或者用了 $proxy_add_x_forwarded_for 让客户端头透传进来,Gin 再怎么设白名单也是纸糊的。反过来,Nginx 配对了,Gin 却没调 SetTrustedProxies,那头字段压根不会被解析。
必须两端对齐:
- Nginx 层:最外层用
$remote_addr覆盖X-Real-IP和X-Forwarded-For;中间层只追加,不解析 - Gin 层:启动时调
engine.SetTrustedProxies,中间件里手动解析头 + 校验来源 IP + 提取真实 IP - 禁止透传危险头:
proxy_set_header Host ""、proxy_set_header X-Forwarded-Host "",防止 Host 头污染或二次伪造
最易被忽略的一点:Kubernetes Ingress 或云厂商 SLB 的回源 IP 段经常变动,别写死在代码里——用配置中心或环境变量注入,上线前务必核对最新 CIDR 列表。


















