欢迎各位大佬批评指正。。谢谢
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;
1
showecho 2019-01-02 07:57:00 +08:00
能取使用代理的访客的真实 ip ?
|
2
KomeijiSatori 2019-01-02 07:58:16 +08:00
会有 $_SERVER 函数不存在的情况吗。。
|
4
KomeijiSatori 2019-01-02 07:58:29 +08:00
@KomeijiSatori 函数->变量
|
5
xmlf OP @KomeijiSatori 万一呢?哈哈哈。。。
请大佬提改进意见,谢谢。 |
6
KomeijiSatori 2019-01-02 08:01:49 +08:00
@xmlf if 改成 switch case 吧
|
7
zhujinliang 2019-01-02 08:09:20 +08:00 via iPhone
建议判断一下反代服务器的 IP 地址,以免请求方直接加 X-FORWADR 头冒充反代随意发送源 IP 地址
|
8
0312birdzhang 2019-01-02 08:35:31 +08:00
@showecho 老哥,这个能取到的话那代理的意义是什么🤔
|
9
lhx2008 2019-01-02 08:39:25 +08:00 via Android
和反代协商好头就行了,我从不用 X-FORWARD 啥的公开头,这样别人私自发 X-FORWARD 头你还得判断哪个是反代加的哪个是用户私加的
|
10
lhx2008 2019-01-02 08:40:57 +08:00 via Android
@KomeijiSatori 直接改成 list 遍历就好吧,代码是一样的
|
12
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 楼 |
13
lhx2008 2019-01-02 08:49:41 +08:00 via Android
|
14
sagaxu 2019-01-02 09:02:32 +08:00 via Android
复制粘贴一把梭
|
15
GuangXiN 2019-01-02 09:02:39 +08:00 via Android
xff 字段可以随便传
|
17
supervipcard 2019-01-02 09:13:11 +08:00
上高匿就玩不了了
|
18
xmlf OP @lhx2008 如果是反代,直接在反代服务器使用
``` location /{ proxy_set_header client-real-ip $remote_addr; } ``` 可否? 同时,在 php 程序里使用上面代码,只是修改一下顺序。 将 if ($key == 'HTTP_CLIENT_IP') 放到最前面进行判断。 |
19
lhx2008 2019-01-02 09:42:21 +08:00 via Android
@xmlf 嗯嗯,没错,nginx 这样写会覆盖掉用户的私发头,所以服务器这边也不用验证 IP 了,是最简单的写法。验证 IP 用防火墙没问题。
不过真实情况还可能会有多级反代,比如 nginx 外面还挂一个 CDN,这样基本上只能在 X-Forward-For 做好验证,因为这个头有标准的,一般第一个是第一级反代加的真实 IP,后面的 IP 真实性就没法保证了。 至于 HTTP 匿名代理的 IP,我也没查有什么办法,不过现在除了爬虫,应该很少人用了,如果 ss 代理的话,不可能获取到用户真实 IP 的,只能获取到代理 IP 当然,你这个代码还没有考虑 IPV6 的情况。 |
20
xmlf OP @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; |
21
lhx2008 2019-01-02 09:52:34 +08:00 via Android
X-FORWORD-FOR 我说错了,应该是最右边那个,但是用户伪造也是妥妥的没问题的,所以多级反代的时候这很难办,只能由最靠近用户的那个反代控制好
|
22
lhx2008 2019-01-02 09:56:59 +08:00 via Android
没有必要写出一个通用的代码,我 PHP 也不怎么会,简单的判断就行了。主要是根据你的程序定,比如你要像 discuz 那种开源出去可能要考虑到所有情况,自己用就随便搞下就没问题的。
如果是要做 IP 查询业务的,比如想穿透 HTTP 代理,可能还要结合自建 dns 啥的技术,只做这些还不够 |
23
ChristopherWu 2019-01-02 15:25:36 +08:00
我做过这样的事情,可以看看: https://yonghaowu.github.io/2018/11/23/get_reql_ip/ 从限流谈到伪造 IP nginx remote_addr
|
24
xmlf OP @ChristopherWu
所以,目前最终方案: 1、修改 nginx 配置文件,添加 location /{ proxy_set_header X-Real-IP $remote_addr; } 2、在主楼我贴出的代码上面加个判断 if ($key == 'HTTP_X_REAL_IP') { $ipAddrList = $value; break; } 目前这样应该没什么问题了。 |
25
ChristopherWu 2019-01-02 17:56:41 +08:00
@xmlf 不太想看 php 的代码,有点多。我经过多方思虑,目前给出的博客应该总结的没有问题~
|
26
liuguang 2019-01-02 18:16:26 +08:00
这段代码明显有漏洞啊。我只要在 HTTP 头里面带某个假 ip 属性,你获取的就都是假的
|
28
rekulas 2019-01-11 10:12:17 +08:00
多年 php 不明白为什么获取 IP 搞这么复杂 我都是直接取 remote addr 客户端发来的任何信息都不可信
|