c.clientip()默认不可信,因其仅信任127.0.0.1/32和::1/128,线上经nginx或cdn后返回代理内网地址(如10.0.1.5)而非真实客户端ip,且不校验请求来源可信性,易被伪造x-forwarded-for绕过;必须显式调用settrustedproxies配置可信代理网段(如"100.64.0.0/10"、"192.168.10.0/24"),并结合私有ip过滤逆序解析x-forwarded-for取首个公网ip。

为什么c.ClientIP()默认不能信
因为 c.ClientIP() 默认只信任 127.0.0.1/32 和 ::1/128,线上走 Nginx 或 CDN 后,它返回的其实是代理内网地址(比如 10.0.1.5),不是真实客户端 IP。更危险的是,它不校验请求来源是否可信,只要头存在就取,等于把伪造权交给了攻击者。
必须显式配置SetTrustedProxies并填对网段
没调用 engine.SetTrustedProxies(),X-Forwarded-For 就是废的。传 nil 会回退到默认值(仅本地回环),传空切片 []string{} 会禁用所有头解析,强制用 RemoteAddr —— 这两种都等于放弃链路校验。
- 阿里云 SLB 回源段是
"100.64.0.0/10",不是/16,写错就全量 fallback 到内网地址 - 腾讯云 CLB 是
"100.64.0.0/11",别和阿里混用 - 自建 Nginx 集群要填实际内网段,比如
"192.168.10.0/24",不能填公网 IP 或域名 - 多个网段用切片传入:
engine.SetTrustedProxies([]string{"100.64.0.0/10", "192.168.10.0/24"})
别在中间件里硬编码白名单或遍历匹配
把白名单写死在中间件里,每次改都要重启;用 for range []string 做 CIDR 匹配,QPS 上千时 CPU 直接打满。真正在意性能和热更新的,得用 net.IPNet + atomic.Value 缓存解析结果,每次只做一次 ipNet.Contains(clientIP) 判断。
- 优先查黑名单再白名单,避免误放行
-
/healthz、/metrics等探针路径必须跳过 IP 校验,否则健康检查失败 - 永远用
net.ParseIP()解析字符串,别直接==比较;IPv6 地址带方括号,得先strings.Trim
后端绝不无条件读X-Forwarded-For最左IP
攻击者发 X-Forwarded-For: 1.2.3.4, 127.0.0.1,你若取第一个,就等于认了伪造 IP。真正该信的是“谁加的这个头”——只有来自可信代理网段的请求,才允许解析其 X-Forwarded-For,且必须逆序遍历、跳过所有私有地址(127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16),取第一个公网 IP。
- 单层代理(Nginx 直连 Go):优先读
X-Real-IP,但前提是 Nginx 配了proxy_set_header X-Real-IP $remote_addr - 多层代理(CDN → Nginx → Go):必须配合
SetTrustedProxies,然后手动逆序解析X-Forwarded-For - 永远不做
strings.Split(header, ",")[0]这种裸操作,没校验就等于开门揖盗











