这段取真实 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;
4568 次点击
所在节点    PHP
28 条回复
lhx2008
2019-01-02 09:52:34 +08:00
X-FORWORD-FOR 我说错了,应该是最右边那个,但是用户伪造也是妥妥的没问题的,所以多级反代的时候这很难办,只能由最靠近用户的那个反代控制好
lhx2008
2019-01-02 09:56:59 +08:00
没有必要写出一个通用的代码,我 PHP 也不怎么会,简单的判断就行了。主要是根据你的程序定,比如你要像 discuz 那种开源出去可能要考虑到所有情况,自己用就随便搞下就没问题的。

如果是要做 IP 查询业务的,比如想穿透 HTTP 代理,可能还要结合自建 dns 啥的技术,只做这些还不够
ChristopherWu
2019-01-02 15:25:36 +08:00
我做过这样的事情,可以看看: https://yonghaowu.github.io/2018/11/23/get_reql_ip/ 从限流谈到伪造 IP nginx remote_addr
xmlf
2019-01-02 17:51:58 +08:00
@ChristopherWu
所以,目前最终方案:
1、修改 nginx 配置文件,添加
location /{
proxy_set_header X-Real-IP $remote_addr;
}
2、在主楼我贴出的代码上面加个判断
if ($key == 'HTTP_X_REAL_IP') {
$ipAddrList = $value;
break;
}
目前这样应该没什么问题了。
ChristopherWu
2019-01-02 17:56:41 +08:00
@xmlf 不太想看 php 的代码,有点多。我经过多方思虑,目前给出的博客应该总结的没有问题~
liuguang
2019-01-02 18:16:26 +08:00
这段代码明显有漏洞啊。我只要在 HTTP 头里面带某个假 ip 属性,你获取的就都是假的
xmlf
2019-01-02 18:21:18 +08:00
@liuguang 你说的是我 24 楼的吗?
rekulas
2019-01-11 10:12:17 +08:00
多年 php 不明白为什么获取 IP 搞这么复杂 我都是直接取 remote addr 客户端发来的任何信息都不可信

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

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

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

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

© 2021 V2EX