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

PHP most accurate / safe way to get real user IP address in 2017

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

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

比如看这道题(2011):How to get the client IP address in PHP?

Tim Kennedy 的回答包含使用类似内容的建议:

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 是不安全的,正如下面的评论所强调的那样:

Do NOT use the above code unless you know EXACTLY what it does! I've seen MASSIVE security holes due to this. The client can set the X-Forwarded-For or the Client-IP header to any arbitrary value it wants. Unless you have a trusted reverse proxy, you shouldn't use any of those values.

因为我不完全了解它的作用,所以我不想冒险。他说不安全,但是没有提供安全的获取用户IP地址的方法。

我试过简单的 $_SERVER['REMOTE_ADDR'];,但是这个 returns IP 错误。我已经测试过了,我的真实 IP 遵循这种模式:78.57.xxx.xxx,但我得到的 IP 地址如下:81.7.xxx.xxx

那你有什么想法吗?

获取客户​​端 IP 地址:

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

Note:: This would work only on live site, because on your local host your ip would be one (1) of the internal ip addresses, like 127.0.0.1 So, its Return ::1

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

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

Example:

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

我使用此代码,它对我有用。看看吧。

<?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,一个工作示例。希望对您有所帮助!

您必须与您的系统操作团队合作(或者如果您也身兼此职,则需要做一些研究)。 header 当您的网络基础设施以特定方式配置时使用 header 检查,其中远程请求者是您的网络设备之一而不是终端 用户。

当您的 Web 服务器位于负载平衡器或防火墙或其他需要询问有效负载以正确处理它的设备后面时,就会发生这种情况。一个例子是当负载平衡器终止 ssl 并将请求转发到没有 ssl 的 Web 服务器时。发生这种情况时,远程地址将成为负载平衡器。它也发生在做同样事情的防火墙设备上。

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

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

关键是我们需要调查您的网络配置,以确保原始请求者的 ip 以何种形式一直连接到您的应用程序代码。

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

使用预制库通常更好,因为其中大部分都已经过活跃社区的彻底检查。其中一些,尤其是已经存在很长时间的那些,内置了更多功能。

但如果您想自己编写代码来学习概念,那么我想没问题。我建议将其打包为一个独立的库并将其作为开源发布:)

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

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

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

你可以永远,永远信任客户。

除非是你...如果控制代理或load-balancer,可以配置它让它丢弃这样的header来自原始请求。
然后,也只有到那时,您才能信任一个例如X-Client-IP header,但实际上,此时没有必要……您的网络服务器也可以配置为用该值替换 REMOTE_ADDR,整个过程对您来说变得透明。

无论我们处于哪一年,情况都会如此......对于与安全相关的任何事情 - 只信任 REMOTE_ADDR
最好的情况是读取 HTTP_ 数据用于 统计目的 ,即使那样 - 确保输入至少一个有效的 IP 地址。

简答:

$ip = $_SERVER['REMOTE_ADDR'];


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

首先,如果某人有意隐藏,就不可能可靠地确定他们的源 IP 地址。即使是今天看起来万无一失的东西,也很快就会有漏洞(如果还没有的话)。因此,下面的任何答案都应被视为 UNTRUSTED,这意味着如果您将所有鸡蛋都放在这个篮子里,请准备好有人利用它或以某种方式规避它。

我不会详细介绍绕过 IP 跟踪的所有方法,因为 它在不断发展。我要说的是,只要您知道 IP 地址可以轻松更改或以其他方式被屏蔽,它就可以成为一个有用的日志记录工具。

现在,要强调的一点是 public IP 地址和私有 IP 地址之间存在差异。在 IPV4 中,路由器通常分配一个 public IP 地址,这是服务器端语言实际可以获取的所有地址,因为它看不到您的客户端 IP 地址。对于服务器,您的计算机不作为 web-space 存在。相反,您的路由器才是最重要的。反过来,您的路由器是唯一关心您的计算机的东西,它会分配一个 private IP address(您的 172...* 地址所属)来完成这项工作.这对你有好处,因为你不能直接访问路由器后面的计算机。

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

我对此进行了测试,它 returns 私有 IP 地址。通常我认为这被广告商用来帮助跟踪网络中的个人用户,连同 public IP 地址。我敢肯定,随着人们想出变通办法或 public 强烈抗议迫使他们提供禁用 WebRTC API 的能力,它很快就会变得毫无用处。但是,目前它适用于在 Chrome 和 Firefox 上启用 JavaScript 的任何人。

更多阅读:

这个怎么样 -

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;
    }

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

如果用户正在使用 VPN 或任何代理,则它不会检测到原始 IP。

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

  2. 以下函数在 4 种不同的测试中有效(家庭网络、VPN、远程连接、public 互联网)。该代码可以用作您项目的基本代码。根据需要修改。

  3. 该函数会验证 IP 地址,但不会验证 IP 范围。这将是您获得客户端IP后的额外测试。

  4. $_SERVER["REMOTE_ADDR"] 并不总是 return 真实的客户端 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;
}