IP 工具包 (iputil)
TrustedProxies
可信代理模式,防请求头伪造的客户端 IP 提取
TrustedProxies
GetClientIP 读取的所有代理头都可以被直连客户端伪造。TrustedProxies 提供带信任边界校验的提取方式,鉴权、限流、封禁等安全决策场景应使用本 API,日志展示场景用 GetClientIP 即可。
构造时必须显式声明"可信代理网段 + 该边缘会强制覆盖的客户端 IP 头"。不提供"来源可信就信任所有厂商头"的宽松模式——代理/CDN 会透传客户端伪造的其它厂商头(例如服务挂在腾讯云 EdgeOne 后时,客户端伪造的 CF-Connecting-IP 会原样到达服务端)。
函数签名
func NewTrustedProxies(trustedHeader string, cidrs ...string) (*TrustedProxies, error)
func (tp *TrustedProxies) ClientIP(r *http.Request) string
func (tp *TrustedProxies) Contains(ip string) bool
func IsIPInCIDRs(ip string, cidrs []string) bool
func IsIPv4(ip string) bool
func IsIPv6(ip string) bool基本用法
// 启动时构建一次: 声明边缘头 + 该边缘的出口网段 (支持 CIDR 或单个 IP)
tp, err := iputil.NewTrustedProxies(
"CF-Connecting-IP", // 部署边缘是 Cloudflare
"173.245.48.0/20", // Cloudflare 官方网段 (示例, 完整列表见 CF 文档)
"103.21.244.0/22",
)
if err != nil {
log.Fatal(err)
}
func rateLimitMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientIP := tp.ClientIP(r) // 不可信来源的伪造头会被忽略
if exceeded(clientIP) {
http.Error(w, "请求过于频繁", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}解析行为
| 场景 | 行为 |
|---|---|
| 直连来源不在可信网段 | 忽略一切转发头,返回直连 IP |
可信头为普通厂商头(CF-Connecting-IP / EO-Client-IP / X-Real-IP 等) | 取该头的值(自动剥离附带端口,校验为合法 IP,否则回退直连 IP) |
可信头为 X-Forwarded-For | 从右往左跳过可信网段条目,取第一个不可信的合法 IP(右侧条目由可信代理逐跳追加,不可伪造);链上全部可信时取最左侧合法 IP;无合法条目回退直连 IP |
| 可信头缺失/非法 | 回退直连 IP,不把任意字符串带进安全决策链路 |
按部署选择可信头:
- 挂 Cloudflare →
"CF-Connecting-IP"+ CF 网段 - 挂腾讯云 EdgeOne →
"EO-Client-IP"+ EdgeOne 回源网段 - 自建 nginx 且配置了
proxy_set_header X-Real-IP $remote_addr→"X-Real-IP"+ nginx 内网地址 - 多级代理链只追加 XFF →
"X-Forwarded-For"+ 全部代理网段
辅助函数
iputil.IsIPInCIDRs("10.1.2.3", []string{"10.0.0.0/8"}) // true; 一次性判断, 高频场景用 NewTrustedProxies 预编译
iputil.IsIPv4("192.168.1.1") // true
iputil.IsIPv6("2001:db8::1") // true相关函数
- GetClientIP - 无信任校验的完整优先级提取(日志/展示场景)