PHP项目源站如何获取CDN转发真实IP

wen PHP项目 28

本文目录导读:

PHP项目源站如何获取CDN转发真实IP

  1. 最标准的方法:使用 X-Forwarded-For
  2. 推荐的生产环境实践:配置白名单 + 校验
  3. 常见CDN使用的特殊请求头
  4. 服务器配置层面的常见方案(推荐优先配置)
  5. 总结建议

在PHP项目中,获取经过CDN转发后的真实用户IP,通常需要检查特定的HTTP请求头,CDN(内容分发网络)在转发请求时,会将客户端的原始IP地址添加到一个标准或自定义的请求头中,最常见的头是 X-Forwarded-For

以下是几种常见的实现方法及最佳实践:

最标准的方法:使用 X-Forwarded-For

X-Forwarded-For (XFF) 是事实上的标准,但其格式可能包含多个IP(client_ip, proxy1_ip, proxy2_ip),你需要获取最左边的IP,因为那是最终客户端的IP。

<?php
function getClientRealIP() {
    $ip = '';
    // 1. 尝试从 X-Forwarded-For 获取(最左边的是真实IP)
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        // 可能包含逗号分隔的多个IP,取第一个
        $ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        $ip = trim($ipList[0]); // 取最左边,即原始客户端IP
    }
    // 2. X-Forwarded-For 不存在,尝试 X-Real-IP(某些CDN如Nginx常用)
    if (empty($ip) && isset($_SERVER['HTTP_X_REAL_IP'])) {
        $ip = $_SERVER['HTTP_X_REAL_IP'];
    }
    // 3. 最后退回到 REMOTE_ADDR(但此时可能是CDN节点IP而非用户IP)
    if (empty($ip)) {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
    }
    // 4. 验证IP格式(防止伪造或无效数据)
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
        return $ip;
    }
    // 如果验证失败,返回一个默认值或REMOTE_ADDR
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
// 使用示例
$userIP = getClientRealIP();
echo "用户真实IP: " . $userIP;
?>

推荐的生产环境实践:配置白名单 + 校验

为什么需要白名单? 直接信任 X-Forwarded-For 存在安全隐患:如果攻击者直接访问你的源站(未经过CDN),他们可以伪造 X-Forwarded-For 头来伪装IP。

解决方案: 只在你信任的CDN代理IP之后,才信任 X-Forwarded-For

<?php
// 定义允许的CDN节点IP或CIDR(需要从你的CDN提供商获取)
$trustedProxies = [
    '103.21.244.0/22', // Cloudflare 示例
    '103.22.200.0/22',
    // ... 实际你需要替换为你使用的CDN(阿里云CDN、腾讯云CDN、Fastly等)
];
// 检查当前 REMOTE_ADDR 是否属于信任代理
function isTrustedProxy($ip) {
    global $trustedProxies;
    foreach ($trustedProxies as $proxyCidr) {
        if (ipInCIDR($ip, $proxyCidr)) {
            return true;
        }
    }
    return false;
}
// 简化版CIDR匹配函数(生产环境建议使用库或更完善的实现)
function ipInCIDR($ip, $cidr) {
    if (strpos($cidr, '/') === false) {
        return $ip === $cidr;
    }
    list($subnet, $mask) = explode('/', $cidr);
    $ipLong = ip2long($ip);
    $subnetLong = ip2long($subnet);
    $maskLong = -1 << (32 - (int)$mask);
    return ($ipLong & $maskLong) === ($subnetLong & $maskLong);
}
function getClientRealIPSafe() {
    // 如果请求来自信任的CDN节点,则信任头信息
    if (isTrustedProxy($_SERVER['REMOTE_ADDR'])) {
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $ipList = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            $ip = trim($ipList[0]);
        } elseif (isset($_SERVER['HTTP_X_REAL_IP'])) {
            $ip = $_SERVER['HTTP_X_REAL_IP'];
        }
    }
    // 默认使用 REMOTE_ADDR(可能是CDN节点IP或用户IP)
    if (empty($ip)) {
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
    }
    if (filter_var($ip, FILTER_VALIDATE_IP)) {
        return $ip;
    }
    return '0.0.0.0';
}
// 实际使用
$userIP = getClientRealIPSafe();
?>

常见CDN使用的特殊请求头

除了 X-Forwarded-For,不同CDN可能使用不同的头:

CDN 服务商 常用头
阿里云 CDN X-Forwarded-For (标准) 或通过 Ali-CDN-Real-IP (旧版)
腾讯云 CDN X-Forwarded-For
Cloudflare CF-Connecting-IP (更推荐) 或 X-Forwarded-For
Fastly X-Forwarded-For
Amazon CloudFront CloudFront-Viewer-Address (格式: ip:port)
Nginx + CDN 插件 X-Real-IP, X-Forwarded-For

你可以针对你使用的CDN增加特定头检查:

// Cloudflare 特别版本
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
    $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
}
// CloudFront 特殊处理
if (isset($_SERVER['HTTP_CLOUDFRONT_VIEWER_ADDRESS'])) {
    $ip = explode(':', $_SERVER['HTTP_CLOUDFRONT_VIEWER_ADDRESS'])[0];
}

服务器配置层面的常见方案(推荐优先配置)

在PHP层面处理是有效的,但更好的做法是在Web服务器(Nginx/Apache)层面做一次转换,直接让PHP看到真实IP,例如在 Nginxserver 块中添加:

# 在 Nginx 配置中(反向代理部分或全局)
set_real_ip_from 103.21.244.0/22;   # 信任的CDN IP段
set_real_ip_from 103.22.200.0/22;
# ... 其他CDN IP段
real_ip_header X-Forwarded-For;
real_ip_recursive on;   # 递归处理:当有多个代理时,取最左有效IP

这样配置后,PHP直接使用 $_SERVER['REMOTE_ADDR'] 就能得到真实用户IP,这是最干净、最安全的方案。

总结建议

方案 适用场景 安全性
Nginx/Apache 层面配置 (推荐) 你拥有服务器配置权限 高,源头处理
PHP 层面 + IP白名单校验 无法修改服务器配置,但能改代码 高(配合白名单)
PHP 层面直接取 XFF 第一个 只有可控内网/测试环境 低(易被伪造)
  1. 优先配置服务器:如果能修改Nginx/Apache配置,在服务器端设置 real_ip_headerset_real_ip_from 是最佳实践。
  2. 如果必须用PHP:请务必加上信任IP白名单(你CDN节点的IP范围),否则恶意客户端可以直接伪造IP。
  3. 始终验证IP格式:使用 filter_var(..., FILTER_VALIDATE_IP) 过滤脏数据。

通过以上方法,你就能可靠地从PHP项目中获取经过CDN转发的真实用户IP。

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