在PHP中,获取用户的正确IP地址最准确的方法是什么?
在PHP中,获取用户的正确IP地址最准确的方法是什么?
有许多$_SERVER变量头可用于获取IP地址。我想知道是否有普遍共识,关于如何使用这些变量最准确地获取用户的真实IP地址(明白没有一种方法是完美的)?
我花了一些时间尝试找到一个深入的解决方案,并根据多个来源编写了以下代码。如果有人能够指出问题或提供更准确的方法,我会非常感激。
编辑包括来自@Alix的优化
/** * 检索客户端实际IP地址的最佳猜测。 * 考虑到由于不同ISP在跳跃之间处理标头中的IP地址的变化而导致的众多HTTP代理标头。 */ public function get_ip_address() { // 检查共享的互联网/ISP IP if (!empty($_SERVER['HTTP_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_CLIENT_IP'])) return $_SERVER['HTTP_CLIENT_IP']; // 检查通过代理传递的IP if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { // 检查var中是否存在多个IP地址 $iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($iplist as $ip) { if ($this->validate_ip($ip)) return $ip; } } } if (!empty($_SERVER['HTTP_X_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_X_FORWARDED'])) return $_SERVER['HTTP_X_FORWARDED']; if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && $this->validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP'])) return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP']; if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && $this->validate_ip($_SERVER['HTTP_FORWARDED_FOR'])) return $_SERVER['HTTP_FORWARDED_FOR']; if (!empty($_SERVER['HTTP_FORWARDED']) && $this->validate_ip($_SERVER['HTTP_FORWARDED'])) return $_SERVER['HTTP_FORWARDED']; // 如果所有方法都失败,则返回不可靠的IP地址 return $_SERVER['REMOTE_ADDR']; } /** * 确保一个IP地址既是有效的IP地址,也不在私有网络范围内。 * * @access public * @param string $ip */ public function validate_ip($ip) { if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) return false; self::$ip = $ip; return true; }
警告(更新)
REMOTE_ADDR
仍然代表IP地址的最可靠来源。这里提到的其他$_SERVER
变量可以被远程客户端轻易伪造。此解决方案的目的是尝试确定代理后面的客户端的IP地址。对于一般用途,您可以考虑将其与直接从$_SERVER['REMOTE_ADDR']
返回的IP地址结合使用,并将两者都存储起来。
对于99.9%的用户,这个解决方案将完全满足您的需求。它无法防止0.1%的恶意用户通过注入自己的请求标头来滥用您的系统。如果依赖IP地址进行一些关键任务,请使用REMOTE_ADDR
,不要费心迎合那些使用代理的用户。
问题的原因是在PHP中获取用户正确的IP地址时存在一些困难,因为$_SERVER变量中包含多个可能的IP地址信息。解决方法是根据常见的HTTP头信息中获取IP地址的优先级来编写代码,以确保返回的IP地址是正确的。
解决方法如下所示:
首先,检查$_SERVER['HTTP_CLIENT_IP']是否存在,如果存在,则返回该IP地址。
如果$_SERVER['HTTP_CLIENT_IP']不存在,则检查$_SERVER['HTTP_X_FORWARDED_FOR']是否存在。如果存在,则使用explode函数将该IP地址字符串按逗号分隔,并返回最后一个IP地址。
如果$_SERVER['HTTP_X_FORWARDED_FOR']也不存在,则返回$_SERVER['REMOTE_ADDR'],即本地主机地址。
此外,由于使用Squid时存在一些奇怪的问题,所以在处理$_SERVER['HTTP_X_FORWARDED_FOR']时需要使用explode函数。
以上代码和解决方法可以确保获取到用户的正确IP地址。
在PHP中获取用户正确的IP地址最准确的方法是使用REMOTE_ADDR变量。然而,即使这样,获取用户真实IP地址仍然不可靠。用户只需使用一个不管http_x_forwarded_for
、http_forwarded
等头部的匿名代理服务器,你将只能获得代理服务器的IP地址。
你可以查看是否有一个匿名代理服务器IP地址的列表,但是无法确定这个列表是否100%准确,最多只能让你知道这是一个代理服务器。而且如果有人聪明地伪造HTTP转发的头部,你将无能为力。
假设我不喜欢当地的大学。我找出他们注册的IP地址,并通过做坏事的方式,使得你的网站禁止他们的IP地址,因为我发现你尊重HTTP转发。这个列表是无穷无尽的。
然后,还有内部IP地址,比如我之前提到的大学网络。很多使用的是10.x.x.x的格式。所以你只会知道它是转发给一个共享网络的。
然后我不会深入讨论,但动态IP地址是宽带的常态。所以,即使你获取到用户的IP地址,预计在2-3个月内,它也会发生变化,最长不会超过这个时间。
感谢你的建议。我目前正在使用用户的IP地址来帮助会话认证,通过使用他们的C类IP作为限制因素来限制会话劫持,但允许合理范围内的动态IP。伪造的IP地址和匿名代理服务器只是我必须为一小部分人解决的问题。
- 当然,对于这个目的来说,REMOTE_ADDR是正确的方法。任何依赖于HTTP头部的方法都容易被伪造。会话的持续时间有多长?动态IP地址不会快速改变。
它们确实会改变,特别是如果我想要改变它们(更改MAC地址,许多驱动程序都支持)。只使用REMOTE_ADDR足以获取它所连接到的最后一个服务器的IP地址。因此,在代理情况下,你会得到代理IP。
通过阅读以上内容,我们可以得出以下结论:
问题的出现原因是PHP中如何准确地获取用户的IP地址。
解决方法是使用给定的代码来获取IP地址。代码中使用了$_SERVER数组中的一些键来获取IP地址,例如HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR等。然后使用foreach循环和explode函数来处理可能存在的多个IP地址,然后使用filter_var函数对IP地址进行验证,确保其是一个有效的IP地址。
此外,还提到了一些优化和改进的建议,例如使用filter_var函数代替validate_ip函数来验证IP地址,简化HTTP_X_FORWARDED_FOR的代码等。还提到了一些关于安全性的问题和建议,以及一些特定情况下的解决方案,例如处理Cloudflare代理的情况。
最后,还提到了一些关于代码兼容性和错误的问题,以及对代码的使用和效果的评论。
通过阅读以上内容,我们可以了解到在PHP中获取用户的准确IP地址的最佳方法,并了解了一些相关的优化和安全性问题。