PHP是2017年获得真实用户IP地址的最准确/最安全的方式

时间:2017-05-20 11:01:39

标签: php security proxy ip reverse-proxy

2017年通过PHP获取用户IP地址的最准确方法是什么?

我已经阅读了很多关于它的SO问题和答案,但大多数答案都是陈旧的,用户评论说这些方法不安全。

例如,请看一下这个问题(2011):How to get the client IP address in PHP?

蒂姆肯尼迪的回答包含一个建议,使用类似的东西:

if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
    $ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
    $ip = $_SERVER['REMOTE_ADDR'];
}

但正如我已经阅读了很多内容,我已经看到使用X_FORWARDED_FOR是不安全的,因为下面的评论强调:

  

除非您完全知道它的作用,否则请勿使用上述代码!我有   因此看到了大规模的安全漏洞。客户端可以设置   X-Forwarded-For或Client-IP标头为任意值   想。除非您有可信的反向代理,否则不应使用任何反向代理   这些价值观。

由于我完全不知道它的作用,我不想承担风险。他说它不安全,但没有提供安全的方法来获取用户的IP地址。

我尝试过简单的$_SERVER['REMOTE_ADDR'];,但这会返回错误的IP。我已经对此进行了测试,我的真实IP遵循以下模式:78.57.xxx.xxx,但我得到的IP地址如下:81.7.xxx.xxx

你有什么想法吗?

10 个答案:

答案 0 :(得分:15)

简答:

$ip = $_SERVER['REMOTE_ADDR'];

2019开始 $_SERVER['REMOTE_ADDR'];获取用户IP地址的唯一可靠方式,但如果在代理服务器后面,则会显示错误的结果。 /> 所有其他解决方案都意味着安全风险,或者容易伪造

答案 1 :(得分:9)

从安全POV,没有$_SERVER['REMOTE_ADDR']是可靠的 - 不幸的是,这只是一个简单的事实。

所有带有HTTP_前缀的变量实际上都是客户端发送的HTTP标头,当请求通过不同的服务器时,没有其他方法可以传输该信息。
但这当然会自动意味着客户可以欺骗那些标题。

永远不会信任客户。

除非是你......如果控制代理或负载均衡器,可以对其进行配置,使其从原始请求中删除这些标题。
然后,只有这样,你可以信任一个例如X-Client-IP标题,但实际上,此时没有必要......您的网络服务器也可以配置为使用该值替换REMOTE_ADDR,整个过程对您来说都是透明的。< / p>

无论我们在哪一年......对于任何与安全相关的事情 - 只信任REMOTE_ADDR,情况总是如此。 最佳情况是阅读统计用途 HTTP_数据,即便如此 - 确保输入至少是有效的IP地址。< / p>

答案 2 :(得分:7)

你必须与你的系统团队合作(或者你也戴着那顶帽子,你需要做一些研究)。当您以特定方式配置网络基础结构时,将使用标头检查,其中远程请求者是您的网络设备之一而不是结束 用户。

当您的Web服务器位于负载均衡器或防火墙或其他需要询问有效负载以正确处理它的设备后面时,就会发生这种情况。例如,当负载均衡器终止ssl并将请求转发到没有ssl的Web服务器时。发生这种情况时,远程地址将成为负载均衡器。防火墙设备也可以执行相同的操作。

大多数情况下,设备将提供配置,以使用原始远程IP地址在请求中设置标头值。标题通常是您所期望的,但在某些情况下可能会有所不同甚至可配置。

此外,根据您的Web服务器配置(apache,nginx或其他),可能不支持或当前配置为支持某些自定义标头,例如公共IP标头。

重点是我们您需要调查您的网络配置,以确保原始请求者的IP能够完全覆盖您的应用程序代码以及以何种形式。

答案 3 :(得分:5)

如果您想使用预建库,可以使用Whip

使用预先制作的库通常会更好,因为大多数库都会被活跃的社区彻底检查过。其中一些,特别是已经存在很长时间的内容,内置了更多功能。

但是如果你想自己编写代码来学习这个概念,那我猜是可以的。我建议将其打包为独立库并将其作为开源源发布:)

编辑:我不建议在安全机制中使用远程IP,因为它们并不总是可靠的。

答案 4 :(得分:4)

首先,如果他们有意隐藏,则无法可靠地确定某人的源IP地址。即使是今天似乎万无一失的东西,很快就会有一个漏洞(如果它还没有)。因此,下面的任何答案都应该被视为 UNTRUSTED ,这意味着如果你把所有的鸡蛋都放在这个篮子里,那就准备好让别人利用它或以某种方式绕过它。

我无法了解某人可以绕过IP跟踪的所有方式,因为 它在不断发展。我要说的是,只要您知道IP地址可以轻易更改或以其他方式被屏蔽,它就可以成为日志记录的有用工具。

现在,最重要的一点是公共IP地址和私有IP地址之间存在差异。在IPV4中,路由器通常被分配一个公共IP地址,这是服务器端语言实际可以获取的全部IP地址,因为它没有看到您的客户端IP地址。对于服务器,您的计算机不作为Web空间存在。相反,您的路由器就是最重要的。反过来,您的路由器是唯一关心您的计算机的东西,它会分配private IP address(您的172. 。*地址所属的)以使其正常工作。这对您有好处,因为您无法直接访问路由器后面的计算机。

如果要访问私有IP地址,则需要使用JavaScript(客户端语言)。然后,您可以通过AJAX异步存储数据。据我所知,目前只能使用支持WebRTC的Chrome和Firefox。有关演示,请参阅here

我对此进行了测试,并返回了私有IP地址。通常我认为广告商使用它来帮助跟踪网络中的各个用户以及公共IP地址。我确信,随着人们提出变通方法或公众强烈抗议迫使他们提供禁用WebRTC API的能力,它很快就会变得毫无用处。但是,目前它适用于在Chrome和Firefox上启用JavaScript的任何人。

更多阅读:

答案 5 :(得分:3)

真正的方法是检查用户IP是$ip = $_SERVER['REMOTE_ADDR'];

如果用户使用VPN或任何代理,则它将无法检测到原始IP。

答案 6 :(得分:2)

获取客户端IP地址:

<?php
 echo   $ip = $_SERVER['REMOTE_ADDR'];
?>
  

注::        这只适用于实际站点,因为在本地主机上你的ip将是内部ip地址的一(1)个,如127.0.0.1        所以,它的Return :: 1

示例: https://www.virendrachandak.com/demos/getting-real-client-ip-address-in-php.php

显示你的本地IP:  喜欢...... 78.57.xxx.xxx

  

示例:

<?php
$myIp= getHostByName(php_uname('n'));
 echo $myIp;
?>

答案 7 :(得分:1)

这个怎么样 -

public function getClientIP()
    {
        $remoteKeys = [
            'HTTP_X_FORWARDED_FOR',
            'HTTP_CLIENT_IP',
            'HTTP_X_FORWARDED',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'REMOTE_ADDR',
            'HTTP_X_CLUSTER_CLIENT_IP',
        ];

        foreach ($remoteKeys as $key) {
            if ($address = getenv($key)) {
                foreach (explode(',', $address) as $ip) {
                    if ($this->isValidIp($ip)) {
                        return $ip;
                    }
                }
            }
        }

        return '127.0.0.0';
    }


    private function isValidIp($ip)
    {
        if (!filter_var($ip, FILTER_VALIDATE_IP,
                FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)
            && !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE)
        ) {
            return false;
        }

        return true;
    }

答案 8 :(得分:0)

我使用此代码,它适用于我。看一看。

<?php

// Gets client's IP.
$ip = getenv("HTTP_CLIENT_IP")?:
getenv("HTTP_X_FORWARDED_FOR")?:
getenv("HTTP_X_FORWARDED")?:
getenv("HTTP_FORWARDED_FOR")?:
getenv("HTTP_FORWARDED")?:
getenv("REMOTE_ADDR");

echo $ip;

?>

Here,一个工作的例子。希望它有所帮助!

答案 9 :(得分:0)

  1. 由于不同的网络设置(代理服务器,专用网络等)以及管理员配置网络的方式,因此很难获得客户端IP地址。有关此问题的标准正在得到解决。

  2. 以下功能在4种不同的测试中起作用(家庭网络,VPN,远程连接,公共互联网)。该代码可用作您的项目的基本代码。根据需要进行修改。

  3. 该功能会验证IP地址,但不会验证IP范围。获得客户端IP后,这将是一项附加测试。

  4. $ _ SERVER [“ REMOTE_ADDR”]并不总是返回真实的客户端IP地址。

  5. 由于某些参数可由最终用户设置,因此安全性可能成为问题。

设置客户端IP地址

$ clientIpAddress = $ du-> setClientIpAddress($ _ SERVER);

public function setClientIpAddress($serverVars) {
    # Initialization
    $searchList = "HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR,HTTP_X_FORWARDED,HTTP_X_CLUSTER_CLIENT_IP,HTTP_FORWARDED_FOR,HTTP_FORWARDED,REMOTE_ADDR";
    $clientIpAddress = "";

    # Loop through parameters
    $mylist = explode(',', $searchList);
    foreach ($mylist as $myItem) {
        # Is our list set?
        if (isset($serverVars[trim($myItem)])) {
            # Loop through IP addresses
            $myIpList = explode(',', $serverVars[trim($myItem)]);
            foreach ($myIpList as $myIp) {
                if (filter_var(trim($myIp), FILTER_VALIDATE_IP)) {
                    # Set client IP address
                    $clientIpAddress = trim($myIp);

                    # Exit loop
                    break;
                }
            }
        }

        # Did we find any IP addresses?
        if (trim($clientIpAddress) != "") {
            # Exit loop
            break;
        }
    }

    # Default (if needed)
    if (trim($clientIpAddress) == "") {
        # IP address was not found, use "Unknown"
        $clientIpAddress = "Unknown";
    }

    # Exit
    return $clientIpAddress;
}