PHP项目多层CDN如何逐层解析访客IP

wen PHP项目 31

本文目录导读:

PHP项目多层CDN如何逐层解析访客IP

  1. 核心原理
  2. 多层CDN解析策略
  3. 集成方案(最佳实践)
  4. 测试验证方法
  5. 安全注意事项
  6. 生产环境配置示例

在 PHP 项目中处理多层 CDN(如 Cloudflare → 阿里云 CDN → 源站)时,需要通过逐层判断 HTTP 头部来获取真实访客 IP,以下是标准实现方案:

核心原理

CDN 通常在请求头中传递原始 IP:

  • X-Forwarded-For:最常用,可包含逗号分隔的多个IP
  • X-Real-IP:部分CDN使用(如Nginx)
  • CDN-Specific-IP:特定CDN的私有头(如 Cloudflare 的 CF-Connecting-IP

多层CDN解析策略

function getRealClientIP() {
    // 1. 检查专用CDN头(优先)
    $cdnHeaders = [
        'HTTP_CF_CONNECTING_IP',      // Cloudflare
        'HTTP_X_FORWARDED_FOR',       // 标准代理
        'HTTP_X_REAL_IP',             // Nginx代理
        'HTTP_X_AKAMAI_EDGESERVERIP', // Akamai
        'HTTP_TRUE_CLIENT_IP',        // Cloudflare Enterprise
        'HTTP_FASTLY_CLIENT_IP',      // Fastly
        'HTTP_INCAP_CLIENT_IP',       // Incapsula
        'HTTP_CLIENT_IP'              // 通用
    ];
    foreach ($cdnHeaders as $header) {
        if (isset($_SERVER[$header]) && !empty($_SERVER[$header])) {
            $ip = $_SERVER[$header];
            // 2. 处理 X-Forwarded-For 多IP情况(从右到左排除可信代理)
            if ($header === 'HTTP_X_FORWARDED_FOR') {
                $ip = parseXForwardedFor($ip);
            }
            // 3. 验证IP有效性
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                return $ip;
            }
        }
    }
    // 4. 最终fallback到REMOTE_ADDR
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
/**
 * 解析X-Forwarded-For多级IP
 * 示例:X-Forwarded-For: client, proxy1, proxy2
 * 策略:取最左侧有效公网IP(排除CDN节点IP)
 */
function parseXForwardedFor($headerValue) {
    $ips = explode(',', $headerValue);
    $ips = array_map('trim', $ips);
    // 定义可信CDN节点IP段(根据实际CDN提供商配置)
    $trustedProxies = [
        '173.245.48.0/20',   // Cloudflare
        '103.21.244.0/22',   // Cloudflare
        '103.22.200.0/22',   // Cloudflare
        '103.31.4.0/22',     // Cloudflare
        '141.101.64.0/18',   // Cloudflare
        '108.162.192.0/18',  // Cloudflare
        '190.93.240.0/20',   // Cloudflare
        '188.114.96.0/20',   // Cloudflare
        '197.234.240.0/22',  // Cloudflare
        '198.41.128.0/17',   // Cloudflare
        '162.158.0.0/15',    // Cloudflare
        '104.16.0.0/12',     // Cloudflare
        '172.64.0.0/13',     // Cloudflare
        '131.0.72.0/22',     // Cloudflare
        // 阿里云CDN
        '106.11.0.0/16',
        '218.30.0.0/15',
        // ... 其他CDN厂商IP段
    ];
    // 从右向左扫描,跳过可信代理IP
    for ($i = count($ips) - 1; $i >= 0; $i--) {
        $ip = $ips[$i];
        if (!isTrustedProxy($ip, $trustedProxies)) {
            return $ip;
        }
    }
    // 如果所有IP都是可信代理,返回最左侧
    return $ips[0] ?? $_SERVER['REMOTE_ADDR'];
}
function isTrustedProxy($ip, $trustedProxies) {
    foreach ($trustedProxies as $cidr) {
        if (ipInCIDR($ip, $cidr)) {
            return true;
        }
    }
    return false;
}
/**
 * CIDR匹配函数
 */
function ipInCIDR($ip, $cidr) {
    list($subnet, $mask) = explode('/', $cidr);
    if ((ip2long($ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($subnet)) {
        return true;
    }
    return false;
}

集成方案(最佳实践)

配置文件(config/cdn.php)

return [
    'trusted_proxies' => [
        // Cloudflare IP ranges
        '173.245.48.0/20',
        '103.21.244.0/22',
        // ... 完整列表从CDN官网获取
        // 阿里云CDN
        '106.11.0.0/16',
        // 自定义内网CDN
        '10.0.0.0/8',
        '172.16.0.0/12',
    ],
    'ip_headers' => [
        'CF-Connecting-IP',
        'X-Real-IP',
        'X-Forwarded-For',
        'True-Client-IP',
    ],
];

中间件实现(Laravel示例)

namespace App\Http\Middleware;
use Closure;
class ResolveClientIP
{
    public function handle($request, Closure $next)
    {
        $clientIP = $this->getClientIP($request);
        $request->merge(['client_ip' => $clientIP]);
        return $next($request);
    }
    private function getClientIP($request)
    {
        // Cloudflare优先
        if ($request->hasHeader('CF-Connecting-IP')) {
            return $request->header('CF-Connecting-IP');
        }
        // X-Forwarded-For
        if ($request->hasHeader('X-Forwarded-For')) {
            $ips = explode(',', $request->header('X-Forwarded-For'));
            // 从右到左排除可信代理
            foreach (array_reverse($ips) as $ip) {
                $ip = trim($ip);
                if (!$this->isTrustedProxy($ip)) {
                    return $ip;
                }
            }
        }
        return $request->ip();
    }
    private function isTrustedProxy($ip)
    {
        $proxies = config('cdn.trusted_proxies');
        foreach ($proxies as $cidr) {
            if ($this->ipInCIDR($ip, $cidr)) {
                return true;
            }
        }
        return false;
    }
}

测试验证方法

测试脚本

// test.php
$_SERVER = [
    'REMOTE_ADDR' => '1.2.3.4',
    'HTTP_X_FORWARDED_FOR' => '203.0.113.5, 198.51.100.1, 192.0.2.1',
    'HTTP_CF_CONNECTING_IP' => '203.0.113.5',
];
echo "Client IP: " . getRealClientIP();
// 输出: 203.0.113.5 (因为X-Forwarded-For最左侧为203.0.113.5)

使用curl模拟测试

# 模拟三层CDN
curl -H "X-Forwarded-For: 真正的公网IP, CDN1节点IP, CDN2节点IP" \
     -H "CF-Connecting-IP: 真正的公网IP" \
     https://yourdomain.com/api/check-ip.php

安全注意事项

  1. 不要信任REMOTE_ADDR:在多CDN环境下,它通常只代表最后一层CDN节点
  2. 定期更新CDN IP段:从各CDN厂商获取最新IP范围
  3. 考虑IP伪造风险:如果HTTP头可被伪造,需要验证请求是否真的经过CDN
  4. 使用HTTPS:防止中间人篡改头部信息
  5. 记录请求链路:建议记录完整IP链用于调试

生产环境配置示例

# Nginx 配置示例
server {
    set_real_ip_from 10.0.0.0/8;    # 内网CDN
    set_real_ip_from 173.245.48.0/20; # Cloudflare
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;  # 关键:开启递归查找
}

通过这种分层策略,可以准确获取经过多层CDN代理后的真实访客IP,同时避免伪造IP攻击,建议在框架层面统一实现并缓存结果,避免每次请求重复计算。

抱歉,评论功能暂时关闭!