fix(security): X-Forwarded-For IP 伪造防护
- isTrustedProxy: 空可信代理列表时默认不信任(安全优先) - realIP: 修正 XFF 遍历逻辑,从右到左跳过可信代理,返回第一个不可信的客户端 IP - GetClientIP: 优先读取 IPFilterMiddleware 已验证的 client_ip,避免直接信任转发头
This commit is contained in:
@@ -10,38 +10,14 @@ import (
|
||||
)
|
||||
|
||||
// GetClientIP 从 Gin Context 中提取客户端真实 IP 地址。
|
||||
// 按以下优先级检查 Header:
|
||||
// 1. CF-Connecting-IP (Cloudflare)
|
||||
// 2. X-Real-IP (Nginx)
|
||||
// 3. X-Forwarded-For (取第一个非私有 IP)
|
||||
// 4. c.ClientIP() (Gin 内置方法)
|
||||
// 优先读取 IPFilterMiddleware 设置的 client_ip(已做代理验证),
|
||||
// 否则回退到 c.ClientIP()(依赖 Gin 的 TrustedProxies 配置)。
|
||||
func GetClientIP(c *gin.Context) string {
|
||||
// 1. Cloudflare
|
||||
if ip := c.GetHeader("CF-Connecting-IP"); ip != "" {
|
||||
return normalizeIP(ip)
|
||||
}
|
||||
|
||||
// 2. Nginx X-Real-IP
|
||||
if ip := c.GetHeader("X-Real-IP"); ip != "" {
|
||||
return normalizeIP(ip)
|
||||
}
|
||||
|
||||
// 3. X-Forwarded-For (多个 IP 时取第一个公网 IP)
|
||||
if xff := c.GetHeader("X-Forwarded-For"); xff != "" {
|
||||
ips := strings.Split(xff, ",")
|
||||
for _, ip := range ips {
|
||||
ip = strings.TrimSpace(ip)
|
||||
if ip != "" && !isPrivateIP(ip) {
|
||||
return normalizeIP(ip)
|
||||
}
|
||||
}
|
||||
// 如果都是私有 IP,返回第一个
|
||||
if len(ips) > 0 {
|
||||
return normalizeIP(strings.TrimSpace(ips[0]))
|
||||
if ip, ok := c.Get("client_ip"); ok {
|
||||
if s, ok := ip.(string); ok && s != "" {
|
||||
return normalizeIP(s)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Gin 内置方法
|
||||
return normalizeIP(c.ClientIP())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user