使用 Cloudlfare 获取 IPV4 以及 IPV6 的测试
前言,最近 Cloudflare 的 IP 获取有些变化,导致获取用户的 IP 出现一些问题,经过测试记录一下
以下解释以 PHP 中的 $_SERVER 中的值为例
一般来说,在不使用反向代理的情况下,我们通常使用 REMOTE_ADDR 获取客户端的 IP
REMOTE_ADDR
但是在使用了反向代理之后,我们使用 HTTP_X_FORWARDED_FOR,因为 REMOTE_ADDR 已经是反向代理服务器的 IP
HTTP_X_FORWARDED_FOR
但是众所周知 HTTP_X_FORWARDED_FOR 值是可以伪造的,而它可以以逗号为连接的多个值,所以很不安全
这时候,在使用 Cloudflare 的情况下,Cloudflare 设置 HTTP_CF_CONNECTING_IP 获取 IP
HTTP_CF_CONNECTING_IP
坑来了,注意以下测试数据
细心的管理员会发现,有些用户的 IP 通过 HTTP_X_FORWARDED_FOR 和 HTTP_CF_CONNECTING_IP 获取均为内网 IP ,例如:251.120.126.7
经过推测,应该是电信运营商为用户分配了 IPV6 地址,但同时也分配了 IPV4 地址,只不过为了节省 IP 资源,IPV4 设置为内网 IP ,这时候 HTTP_CF_CONNECTING_IP 值就准确了
怎么办呢?
这个时候 Cloudfalre 请求中设置的 HTTP_CF_CONNECTING_IPV6 就要登场了,因为在以上 HTTP_CF_CONNECTING_IP 为内网地址的情况下,HTTP_CF_CONNECTING_IPV6 会显示真实的 IPV6 地址
HTTP_CF_CONNECTING_IPV6
所以,如果你有相关业务需求,你可以编写一个函数,以下以 Laravel 框架公共函数来处理。注意,这是在使用 Cloudflare 的情况下,其他 CDN 服务商需要参考对应文档
function getClientIp() { $request = request(); // 尝试获取IPv4地址 $ipv4 = $request->header('CF-Connecting-IP'); if ($ipv4 && filter_var($ipv4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return $ipv4; } // 如果IPv4无效,尝试获取IPv6地址 $ipv6 = $request->header('CF-Connecting-IPv6'); if ($ipv6 && filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return $ipv6; } // 如果Cloudflare的头部都无效,尝试使用X-Forwarded-For $forwardedIp = $request->header('X-Forwarded-For'); if ($forwardedIp) { $ips = explode(',', $forwardedIp); $firstIp = trim($ips[0]); if (filter_var($firstIp, FILTER_VALIDATE_IP)) { return $firstIp; } } // 最后,使用Laravel的getClientIp方法 return $request->getClientIp(); }
如果你没有使用任何 php 框架,你可以使用以下函数,参数 $includeIpv6 用于是否启用 IPV6
function getClientIp($includeIpv6 = 0) { // 尝试获取Cloudflare的IPv4地址 if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { $ip = $_SERVER['HTTP_CF_CONNECTING_IP']; if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return $ip; } } // 如果includeIpv6为真,并且IPv4无效,尝试获取Cloudflare的IPv6地址 if ($includeIpv6 && isset($_SERVER['HTTP_CF_CONNECTING_IPV6'])) { $ip = $_SERVER['HTTP_CF_CONNECTING_IPV6']; if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { return $ip; } } // 尝试使用X-Forwarded-For if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($ips as $ip) { $ip = trim($ip); if ($includeIpv6) { if (filter_var($ip, FILTER_VALIDATE_IP)) { return $ip; } } else { if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return $ip; } } } } // 尝试使用其他常见的服务器变量 $ipSources = ['HTTP_CLIENT_IP', 'HTTP_X_REAL_IP', 'REMOTE_ADDR']; foreach ($ipSources as $source) { if (isset($_SERVER[$source])) { $ip = $_SERVER[$source]; if ($includeIpv6) { if (filter_var($ip, FILTER_VALIDATE_IP)) { return $ip; } } else { if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { return $ip; } } } } // 如果所有方法都失败,返回null或者一个默认IP return null; // 或者返回一个默认IP,比如 '0.0.0.0' }
最后感谢 Cloudflare ,致敬!