go-web-utils
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 - 无信任校验的完整优先级提取(日志/展示场景)