CloudFlare 并通过 PHP 记录访问者 IP 地址

Posted

技术标签:

【中文标题】CloudFlare 并通过 PHP 记录访问者 IP 地址【英文标题】:CloudFlare and logging visitor IP addresses via in PHP 【发布时间】:2013-02-05 18:55:50 【问题描述】:

我正在尝试使用 php$_SERVER['REMOTE_ADDR'] 跟踪和记录访问我的网站的用户/访问者。 PHP中IP地址跟踪的典型方法。

但是,我使用 CloudFlare 进行缓存等,并以 CloudFlare 的形式接收它们的 IP 地址:

108.162.212.* - 108.162.239.*

在仍在使用 CloudFlare 的同时检索实际用户/访问者 IP 地址的正确方法是什么?

【问题讨论】:

【参考方案1】:

最好在线检查 cloudflare ip 范围(因为它可能随时更改)然后检查它是否来自 cloudflare。

Cloudflare IP txt

如果源是 Cloudflare,您可以使用 $_SERVER['HTTP_CF_CONNECTING_IP'] 获取您的客户端请求 IP 地址,但将其用于所有请求并不安全,因为它可以由任何用户在请求标头中发送来欺骗您。

您可以使用以下代码获取客户端请求的真实 IP 地址:

function _getUserRealIP() 
    $ipaddress = '';
    if(isset($_SERVER['REMOTE_ADDR']))
        $ipaddress = $_SERVER['REMOTE_ADDR'];
    else if (isset($_SERVER['HTTP_CLIENT_IP']))
        $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
    else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))
        $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
    else if(isset($_SERVER['HTTP_X_FORWARDED']))
        $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
    else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))
        $ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
    else if(isset($_SERVER['HTTP_FORWARDED_FOR']))
        $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
    else if(isset($_SERVER['HTTP_FORWARDED']))
        $ipaddress = $_SERVER['HTTP_FORWARDED'];
    else
        $ipaddress = 'UNKNOWN';
    return $ipaddress;


function _readCloudflareIps()

    $file = file("https://www.cloudflare.com/ips-v4",FILE_IGNORE_NEW_LINES);
    return $file;


function _checkIpInRange($ip, $range) 
    if (strpos($range, '/') == false)
        $range .= '/32';

    // $range is in IP/CIDR format eg 127.0.0.1/24
    list($range, $netmask) = explode('/', $range, 2);
    $range_decimal = ip2long($range);
    $ip_decimal = ip2long($ip);
    $wildcard_decimal = pow(2, (32 - $netmask)) - 1;
    $netmask_decimal = ~ $wildcard_decimal;
    return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));


function _checkIsCloudflare($ip) 
    $cf_ips = _readCloudflareIps();
    $is_cf_ip = false;
    foreach ($cf_ips as $cf_ip) 
        if (_checkIpInRange($ip, $cf_ip)) 
            $is_cf_ip = true;
            break;
        
    
    return $is_cf_ip;


function getRealIp()

    $httpIp = _getUserRealIP();
    $check = _checkIsCloudflare($httpIp);
    if ($check) 
        return $_SERVER['HTTP_CF_CONNECTING_IP'];
    else
        return $httpIp;
    

将此函数导入您的代码后,您只需调用getRealIp() 函数,如:

$userIp = getRealIp();
echo $userIp();

【讨论】:

【参考方案2】:

Cloudflare 可以选择在域的网络管理页面的标头中使用“伪 IPv4”地址。您可以选择添加或覆盖发送到服务器的标头。

来自文档:

什么是伪 IPv4?

作为加速采用 IPv6 的权宜之计,Cloudflare 提供了 Pseudo IPv4,它在需要 IPv4 地址的旧应用程序中支持 IPv6 地址。目标是使用 E 类 IPv4 地址空间为每个 IPv6 地址提供几乎唯一的 IPv4 地址,该地址空间被指定为实验性的,通常不会看到流量。了解更多see here。

选项

添加标题:仅添加额外的Cf-Pseudo-IPv4 标题 覆盖标头:用伪 IPv4 地址覆盖现有的 Cf-Connecting-IPX-Forwarded-For 标头。

注意:我们建议将此设置保留为“关闭”,除非您有特殊需要。

您可以了解有关此功能的更多信息 from this article from Cloudflare,其中更详细地介绍了他们如何尝试在 IPv4 中的 32 位空间中容纳 IPv6 的 128 位地址空间。

如果您想知道 IPv6 地址和伪 IPv4,Cloudflare 会在启用伪 IPv4 时添加 Cf-Connecting-IPv6 标头。这可用于衡量有多少流量来自 IPv6 网络上的设备,如果在投资更新仍受 IPv4 限制的系统之前需要一个可靠的数字来显示管理,这将很有用。

【讨论】:

感谢您的解释。我尝试了 Pseudo IPv4,但它似乎不是实际的 IPv4 地址。我可以得到246.101.74.149,但不是我的IP。我查了IP,得到bogon: trueReserved RFC3330 只有 IPv6 的网络连接没有分配给它们的 IPv4 地址,除非中间设备为它们分配一个虚拟 IP。您如何确定您的 IPv4 地址? 我使用在线工具检查(这台)计算机 IPv4:whatismyipaddress.com,这与我路由器网站上显示的 IP 相同。 我尝试了一些设备。这是我的结果:我的电脑有 WiFi:虚拟 IP,另一台电脑有相同的 WiFi:虚拟 IP,我的手机有相同的 WiFi:正确的 IP,我的手机连接到移动数据:虚拟 IP。 两天前,我没有收到您的任何回复。我将全额奖励现有的最佳答案。我可以说的唯一结论是,没有什么可以得到只有实际的 IPv4,因为我的计算机是伪 IPv4 可以给我虚拟 IPv4 的一个例子。【参考方案3】:

在 Laravel 中将以下行添加到 AppServiceProvider:

...
use Symfony\Component\HttpFoundation\Request;


...
public function boot()

    Request::setTrustedProxies(['REMOTE_ADDR'], Request::HEADER_X_FORWARDED_FOR);

现在您可以使用request()->ip() 获取真实的客户端 IP。

阅读更多here。

【讨论】:

【参考方案4】:

可用于云闪现的额外服务器变量是:

$_SERVER["HTTP_CF_CONNECTING_IP"]真实访客ip地址,这就是你想要的

$_SERVER["HTTP_CF_IPCOUNTRY"] 访问者所在国家/地区

$_SERVER["HTTP_CF_RAY"]

$_SERVER["HTTP_CF_VISITOR"]这可以帮助你知道它是http还是https

你可以这样使用它:

if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) 
  $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];

如果您这样做,并且访问 IP 地址的有效性很重要,您可能需要验证 $_SERVER["REMOTE_ADDR"] 是否包含实际有效的 cloudflare IP 地址,因为如果他能够直接连接,任何人都可以伪造标头到服务器IP。

【讨论】:

+1 用于将 REMOTE_ADDR 替换为真实 IP,因此无需对代码进行其他修改。 如果安全很重要,一个应该检查请求是否来自 Cloudflare IP 范围 (cloudflare.com/ips)。就像,例如在这个 Joomla!插件:github.com/piccaso/joomla-cf/blob/master/cf/cf.php Cloudflare 在其网站上也列出了此代码。 support.cloudflare.com/hc/en-us/articles/… 最佳答案,但也感谢包含 HTTP_CF_IPCOUNTRY,我以前没有遇到过。 @michael 那是因为它返回了你的 ipv6。【参考方案5】:

更新:CloudFlare 为 apache 发布了一个模块mod_cloudflare,该模块将记录并显示实际的访问者 IP 地址,而不是 cloudflare 访问的那些! https://www.cloudflare.com/resources-downloads#mod_cloudflare(回复:olimortimer)

如果您无法访问 apache 运行时,您可以使用下面的脚本,这将允许您检查连接是否通过 cloudflare 并获取用户 ip。

我正在重写我用于另一个问题“CloudFlare DNS / direct IP identifier”的答案

Cloudflare 的 ip 是公开存储的,因此您可以查看它们 here 然后检查 ip 是否来自 cloudflare(这将允许我们从 http 标头 HTTP_CF_CONNECTING_IP 获取真实 ip)。

如果您使用它来禁用所有非 cf 连接,反之亦然,我建议您有一个 php 脚本文件,该文件在所有其他脚本(如 common.php 或 pagestart.php 等)之前被调用。

function ip_in_range($ip, $range) 
    if (strpos($range, '/') == false)
        $range .= '/32';

    // $range is in IP/CIDR format eg 127.0.0.1/24
    list($range, $netmask) = explode('/', $range, 2);
    $range_decimal = ip2long($range);
    $ip_decimal = ip2long($ip);
    $wildcard_decimal = pow(2, (32 - $netmask)) - 1;
    $netmask_decimal = ~ $wildcard_decimal;
    return (($ip_decimal & $netmask_decimal) == ($range_decimal & $netmask_decimal));


function _cloudflare_CheckIP($ip) 
    $cf_ips = array(
        '199.27.128.0/21',
        '173.245.48.0/20',
        '103.21.244.0/22',
        '103.22.200.0/22',
        '103.31.4.0/22',
        '141.101.64.0/18',
        '108.162.192.0/18',
        '190.93.240.0/20',
        '188.114.96.0/20',
        '197.234.240.0/22',
        '198.41.128.0/17',
        '162.158.0.0/15',
        '104.16.0.0/12',
    );
    $is_cf_ip = false;
    foreach ($cf_ips as $cf_ip) 
        if (ip_in_range($ip, $cf_ip)) 
            $is_cf_ip = true;
            break;
        
     return $is_cf_ip;


function _cloudflare_Requests_Check() 
    $flag = true;

    if(!isset($_SERVER['HTTP_CF_CONNECTING_IP']))   $flag = false;
    if(!isset($_SERVER['HTTP_CF_IPCOUNTRY']))       $flag = false;
    if(!isset($_SERVER['HTTP_CF_RAY']))             $flag = false;
    if(!isset($_SERVER['HTTP_CF_VISITOR']))         $flag = false;
    return $flag;


function isCloudflare() 
    $ipCheck        = _cloudflare_CheckIP($_SERVER['REMOTE_ADDR']);
    $requestCheck   = _cloudflare_Requests_Check();
    return ($ipCheck && $requestCheck);


// Use when handling ip's
function getRequestIP() 
    $check = isCloudflare();

    if($check) 
        return $_SERVER['HTTP_CF_CONNECTING_IP'];
     else 
        return $_SERVER['REMOTE_ADDR'];
    

使用脚本非常简单:

$ip = getRequestIP();
$cf = isCloudflare();

if($cf) echo "Connection is through cloudflare: <br>";
else    echo "Connection is not through cloudflare: <br>";

echo "Your actual ip address is: ". $ip;
echo "The server remote address header is: ". $_SERVER['REMOTE_ADDR'];

这个脚本会告诉你真实的IP地址以及请求是否是CF!

【讨论】:

这是正确答案,但请记住,您需要定期更新 IP。上面代码中的当前代码已经无效,因此代码将失败。您可以在这里获取最新的 IP:cloudflare.com/ips 理想情况下,您应该将它们存储在一个文件中,这样您只需更新外部文件,或者更好的是,定期从这里获取它们:cloudflare.com/ips-v4【参考方案6】:

很难将 HTTP_CF_CONNECTING_IP 转换为 REMOTE_ADDR。所以你可以使用 apache (.htaccess) 自动前置来做到这一点。这样您就无需考虑$_SERVER['REMOTE_ADDR']在所有PHP脚本中是否具有正确的值。

.ht 访问代码

php_value auto_prepend_file "/path/to/file.php"

php 代码(file.php)

<?php

define('CLIENT_IP', isset($_SERVER['HTTP_CF_CONNECTING_IP']) ? $_SERVER['HTTP_CF_CONNECTING_IP'] : $_SERVER['REMOTE_ADDR']);

了解更多here

【讨论】:

【参考方案7】:

当您使用 CloudFlare 时,您的服务器和用户之间的所有请求都通过 CloudFlare 服务器进行路由。

在这种情况下,有两种方法可以获取用户的真实 IP 地址:

    通过 CloudFlare 服务器添加的额外服务器标头 在您的服务器上添加 CloudFlare Apache/nginx 模块。

方法一:通过额外的Server Headers获取IP

您可以使用以下代码获取用户的IP地址:

$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"]) $_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);

它是如何工作的?

CloudFlare 在请求中添加了一些额外的服务器变量,如下所示:

$_SERVER["HTTP_CF_CONNECTING_IP"] - 用户的真实IP地址

$_SERVER["HTTP_CF_IPCOUNTRY"] - 用户的 ISO2 国家/地区

$_SERVER["HTTP_CF_RAY"] 用于登录的特殊字符串

在上面的代码中,我们检查是否设置了$_SERVER["HTTP_CF_CONNECTING_IP"]。如果它在那里,我们将考虑作为用户的 IP 地址,否则我们将使用默认代码为$_SERVER['REMOTE_ADDR']

方法 2:在您的服务器上安装 Cloudflare 模块

Installing CloudFlare Module on Apache Server Installing CloudFlare Module on NGINX Server Installing CloudFlare Module on EasyApache+CPanel

【讨论】:

【参考方案8】:

HTTP_CF_CONNECTING_IP 仅在您使用 cloudflare 时才有效,也许您转移您的网站或删除 cloudflare 您会忘记该值,因此请使用此代码。

$ip=$_SERVER["HTTP_CF_CONNECTING_IP"];
if (!isset($ip)) 
  $ip = $_SERVER['REMOTE_ADDR'];

【讨论】:

【参考方案9】:

对于 magento 1.x 用户(我还没有尝试过 magento 2.0),检查 https://tall-paul.co.uk/2012/03/13/magento-show-remote-ip-in-cloudflare-the-right-way/ 需要更改 app/etc/local.xml 并添加: HTTP_CF_CONNECTING_IP

【讨论】:

【参考方案10】:

Cloudflare 会向您的服务器发送一些额外的请求标头,包括 CF-Connecting-IP,我们可以将其存储到 $user_ip(如果已定义),使用这个简单的单线:

$user_ip = (isset($_SERVER["HTTP_CF_CONNECTING_IP"])?$_SERVER["HTTP_CF_CONNECTING_IP"]:$_SERVER['REMOTE_ADDR']);

【讨论】:

问题在于这只是一个简单的标头,因此任何人都可以将其设置为他们想要的任何 IP,这是一个巨大的安全漏洞。 @rafark 如果您的服务器配置正确且位于 Cloudflare 之后,则不会。 是的,当然。这本身就是一个很好的解决方案,但在使用 HTTP_CF_CONNECTING_IP 之前仍应进行检查,如Callum's answer @rafark 我也同意,但这是非常多的 PHP 代码,它们已经以另一种形式存在于您的 Apache 配置中(我希望),并且该代码更便于调试,如果攻击者可以访问你的调试环境我认为你有更重要的问题要处理。【参考方案11】:

自从提出并回答了这个问题后,CloudFlare 为 Apache 发布了mod_cloudflare,它记录并显示了实际的访问者 IP 地址,而不是 CloudFlare 地址:

https://www.cloudflare.com/resources-downloads#mod_cloudflare

【讨论】:

2019 - 他们不再支持mod_cloudflare,但它可以使用。有一个新的东西叫做mod_remoteip。参考:support.cloudflare.com/hc/en-us/articles/… @sinaza 可能最好创建一个新答案,因为我对 mod_cloudflare 的答案是 4 年前的 @sjagr 关于您的编辑,它们是不正确的 - CloudFlare 实际上是 Cloudflare,正如我最初发布的那样

以上是关于CloudFlare 并通过 PHP 记录访问者 IP 地址的主要内容,如果未能解决你的问题,请参考以下文章

如何在CloudFlare下Nginx实现访客真实IP网站日志?

如何设置 virtualmin 以使用 cloudflare

cloudflare api 将 curl 转换为 php curl 并发送 CNAME 更新

Cloudflare 开启时 Virtualmin-Webmin 无法访问

仅限 Apache 2.4 白名单 Cloudflare

确定访问者是不是通过 CloudFlare Pro 上的 SSL 连接 [关闭]