这段取真实 IP 代码有没有需要优化或改进的地方?

2019-01-02 06:46:42 +08:00
 xmlf

欢迎各位大佬批评指正。。谢谢

if (isset($_SERVER)) {
 	// Use $_SERVER variables by preference
 	$HTTP_VARS = $_SERVER;
} else if (isset($_ENV)) {
 	// Fallback to PHP environment variables
 	$HTTP_VARS = $_ENV;
} else $HTTP_VARS = array();

// Step through the captured $_SERVER or $_ENV array, ignoring the case of the keys.
// (Some "authorities" indicate that the keys can be lower or mixed case!)
$ipAddrList = 'unknown';
foreach($HTTP_VARS as $key => $value) {
 	$key = strtoupper($key);
	$value = str_replace(' ', '', $value);// Get rid of embedded blanks

	if ($key == 'HTTP_FORWARDED') {
		// We're dealing with the new HTTP Forwarded: by=identifier; for=identifier; host=host; proto=protocol
		// See: https://tools.ietf.org/html/rfc7239
	    $value = str_replace(',for=', ',', strtolower($value));	// Make everything lower-case and then get rid of extraneous "for=" tags
	    $params = explode(';', $value);// Separate the Forwarded: parameters into an array
	    foreach ($params as $key => $value) {
		    if (substr($value,0,4) == 'for=') {
			    $ipAddrList = substr($value,4);// Everything after "for=" is now a comma-separated list of IPv4 or IPv6 addresses
			    break;
		    }
	    }
	    break;
    }
	if ($key == 'HTTP_X_FORWARDED_FOR') {
	    $ipAddrList = $value;
	    break;
    }
	if ($key == 'HTTP_X_FORWARDED') {
	    $ipAddrList = $value;
	    break;
    }
	if ($key == 'HTTP_FORWARDED_FOR') {
	    $ipAddrList = $value;
	    break;
    }
	if ($key == 'HTTP_CLIENT_IP') {
	    $ipAddrList = $value;
	    break;
    }
	if ($key == 'REMOTE_ADDR') {
	    $ipAddrList = $value;
	    break;
    }
}

$ip = preg_replace('~,.*~', '', $ipAddrList);	// Trim everything after the first comma, leaving just the first IPv4 or IPv6 address

$ip = str_replace(array('"', "'"), '', $ip);	// Get rid of quotation marks used in some addresses
if (substr($ip,0,1) == '[') {
	$ip = preg_replace('~\]:.*~', '', $ip);	// Get rid of IPv6 port number that follows closing square bracket
	$ip = str_replace(array('[', ']'), '', $ip);	// Get rid of square brackets enclosing IPv6 address
} else {
	$ip = preg_replace('~:.*~', '', $ip); // Get rid of IPv4 port number that follows last digit of address
}

unset($HTTP_VARS, $key, $value, $params, $ipAddrList); // We don't need this any more
$_SERVER['REMOTE_ADDR'] = $ip;
4417 次点击
所在节点    PHP
28 条回复
showecho
2019-01-02 07:57:00 +08:00
能取使用代理的访客的真实 ip ?
KomeijiSatori
2019-01-02 07:58:16 +08:00
会有 $_SERVER 函数不存在的情况吗。。
xmlf
2019-01-02 07:58:22 +08:00
@showecho 是的。或者也可以用作 使用反代后,后端取用户真实 IP。
KomeijiSatori
2019-01-02 07:58:29 +08:00
@KomeijiSatori 函数->变量
xmlf
2019-01-02 08:00:38 +08:00
@KomeijiSatori 万一呢?哈哈哈。。。
请大佬提改进意见,谢谢。
KomeijiSatori
2019-01-02 08:01:49 +08:00
@xmlf if 改成 switch case 吧
zhujinliang
2019-01-02 08:09:20 +08:00
建议判断一下反代服务器的 IP 地址,以免请求方直接加 X-FORWADR 头冒充反代随意发送源 IP 地址
0312birdzhang
2019-01-02 08:35:31 +08:00
@showecho 老哥,这个能取到的话那代理的意义是什么🤔
lhx2008
2019-01-02 08:39:25 +08:00
和反代协商好头就行了,我从不用 X-FORWARD 啥的公开头,这样别人私自发 X-FORWARD 头你还得判断哪个是反代加的哪个是用户私加的
lhx2008
2019-01-02 08:40:57 +08:00
@KomeijiSatori 直接改成 list 遍历就好吧,代码是一样的
xmlf
2019-01-02 08:42:37 +08:00
@lhx2008 求大佬给段代码学习学习。
xiangyuecn
2019-01-02 08:49:33 +08:00
直接用 REMOTE_ADDR,或我们可信的请求头(存在反向代理的话)。一来就用 X_FORWARDED_FOR ? 人家都不用去用代理了,直接伪造 X_FORWARDED_FOR 就能绕过系统检测。。

获取真实 IP 感觉是个高深的技术活,贴我一篇早年的 csdn 的帖子,https://bbs.csdn.net/topics/390727207,参考 16、21、32 楼
lhx2008
2019-01-02 08:49:41 +08:00
@xmlf
if 自定义头存在
return 自定义头的 IP
else
return 真实 IP

还有也可以再加个反代的 IP 白名单,其他 IP 拒绝
如果你没有能力控制反代的行为,才需要做适配性的兼容
sagaxu
2019-01-02 09:02:32 +08:00
复制粘贴一把梭
GuangXiN
2019-01-02 09:02:39 +08:00
xff 字段可以随便传
xmlf
2019-01-02 09:12:58 +08:00
@lhx2008 后端服务器直接用 iptables 设置了反代白名单。这样可否?
supervipcard
2019-01-02 09:13:11 +08:00
上高匿就玩不了了
xmlf
2019-01-02 09:34:41 +08:00
@lhx2008 如果是反代,直接在反代服务器使用
```
location /{
proxy_set_header client-real-ip $remote_addr;
}
```
可否?
同时,在 php 程序里使用上面代码,只是修改一下顺序。
将 if ($key == 'HTTP_CLIENT_IP') 放到最前面进行判断。
lhx2008
2019-01-02 09:42:21 +08:00
@xmlf 嗯嗯,没错,nginx 这样写会覆盖掉用户的私发头,所以服务器这边也不用验证 IP 了,是最简单的写法。验证 IP 用防火墙没问题。

不过真实情况还可能会有多级反代,比如 nginx 外面还挂一个 CDN,这样基本上只能在 X-Forward-For 做好验证,因为这个头有标准的,一般第一个是第一级反代加的真实 IP,后面的 IP 真实性就没法保证了。

至于 HTTP 匿名代理的 IP,我也没查有什么办法,不过现在除了爬虫,应该很少人用了,如果 ss 代理的话,不可能获取到用户真实 IP 的,只能获取到代理 IP

当然,你这个代码还没有考虑 IPV6 的情况。
xmlf
2019-01-02 09:51:07 +08:00
@lhx2008 非常感谢大佬指点。为了给后面人更明确和清晰的答案。能否有请大佬将代码最终完善,并贴出全部代码?
拜谢!
另外,在我上面贴出的代码最后就是考虑了 IPV6 的。

$ip = preg_replace('~,.*~', '', $ipAddrList); // Trim everything after the first comma, leaving just the first IPv4 or IPv6 address

$ip = str_replace(array('"', "'"), '', $ip); // Get rid of quotation marks used in some addresses
if (substr($ip,0,1) == '[') {
$ip = preg_replace('~\]:.*~', '', $ip); // Get rid of IPv6 port number that follows closing square bracket
$ip = str_replace(array('[', ']'), '', $ip); // Get rid of square brackets enclosing IPv6 address
} else {
$ip = preg_replace('~:.*~', '', $ip); // Get rid of IPv4 port number that follows last digit of address
}

unset($HTTP_VARS, $key, $value, $params, $ipAddrList); // We don't need this any more
$_SERVER['REMOTE_ADDR'] = $ip;

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/522974

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX