【干货分享】获取用户IP的正确姿势

【干货分享】获取用户IP的正确姿势

如何获取用户的IP,这个需求简直是太常见了,像登录入口,注册入口,投票,日志记录,api接口中判断同一个ip单位时间内的请求数,可是怎么去获取用户的真实IP呢?网上的代码很多,好多人直接拿来就用,却没有想到带来了很大的安全问题。1 代码示例

1

2

3

4

5

6

7

8

9

10

if(!empty($_SERVER['HTTP_CLIENT_IP'])){

$myip = $_SERVER['HTTP_CLIENT_IP'];

}else if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){

$myip = $_SERVER['HTTP_X_FORWARDED_FOR'];

}else{

$myip= $_SERVER['REMOTE_ADDR'];

}

echo $myip;

?>

这是网上的一个示范例子,我们很多同事也这么写,上面这个例子是php实现的,由于HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR,HTTP_X_FORWARDED,HTTP_X_CLUSTER_CLIENT_IP,HTTP_FORWARDED_FOR,HTTP_FORWARDED,HTTP_VIA (经过的 Proxy)这些以HTTP打头的server变量都是用户可控的,由此可导致xss,认证绕过等缺陷。

下面我们看下python的例子:

1

2

3

4

5

6

7

8

def get_ip(request):

try:

return request.META['HTTP_X_FORWARDED_FOR']

except KeyError:

try:

return request.META['HTTP_X_REAL_IP']

except KeyError:

return request.META.get('REMOTE_ADDR', None)

也是由于客户端变量可控导致获取的ip可为任意值。在此例中,X-Real-IP是nginx特有的,通过配置proxy_set_header X-Real-IP $remote_addr;从REMOTE_ADDR中取值。

2 X-Forwarded-For和 REMOTE_ADDR的区别

REMOTE_ADDR代表着客户端的IP,但是这个客户端是相对服务器而言的,也就是实际上与服务器相连的机器的IP(建立tcp连接的那个),这个值是不可伪造的,如果没有代理的话,这个值就是用户实际的IP值,有代理的话,用户的请求会经过代理再到服务器,这个时候REMOTE_ADDR会被设置为代理机器的IP值。正如前面所说,有了代理就获取不了用户的真实IP,由此X-Forwarded-For应运而生,它是一个非正式协议,在请求转发到代理的时候代理会添加一个X-Forwarded-For头,将连接它的客户端IP(也就是你的上网机器IP)加到这个头信息里,这样末端的服务器就能获取真正上网的人的IP了。假设用户的请求顺序如下:网民电脑ip->代理服务器1–>代理服务器2–>目标服务器REMOTE_ADDR:代理服务器2的IP值X-Forwarded-For就是:网民电脑IP,代理1的IP,代理2的IP在这里只有REMOTE_ADDR是可信的,其他从客户端获取的数据都是不可信的,都是可伪造的。下面简单示例下一个篡改X-Forwarded-For的情况:

1

2

3

4

5

6

7

8

9

10

GET / HTTP/1.1

Host: www.myip.cn

Cache-Control: max-age=0

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) *** WebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

Accept-Encoding: gzip, deflate, sdch

Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2

Cookie: Hm_lvt_380ffd3c2225d34ca2087c6970395366=1473755162; Hm_lpvt_380ffd3c2225d34ca2087c6970395366=1473755299; sc_is_visitor_unique=rx4067297.1473755300.43C8C2ACB3CA4FAAEB8885235516D36A.1.1.1.1.1.1.1.1.1

X-Forwarded-For: 127.0.0.111111

返回信息是:

1

您的IP地址: 127.0.0.111111

3 正确的代码示例

在X-Forwarded-For信息头中可以提取真实的用户IP,但是这个IP是可以伪造的,如果从X-Forwarded-For提取IP作为用户的IP对于存在登录次数,api速率限制等一些接口是致命的缺陷,因为任意构造出无数的合法或者非法IP地址。而REMOTE_ADDR只是服务器前端的IP地址,如果没有代理就是用户的真实地址。这个是不可伪造的,而且代理是有限的,可以基于此来获取IP。在wordpress中,获取客户的IP地址代码如下:

1

$remote_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] );

如果是python代码的话:

remote_ip = request.META.get(‘REMOTE_ADDR’, None)当然上述代码也存在缺陷,就是服务器端开了nginx反向代理的时候,每次获取的都是反向代理的IP,这不是我们的预期,需要nginx在配置反向代理的时候做一定设置并且修改代码。如:

1

2

3

proxy_set_header Host $host;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

或者采用realip模块,配置如下:

1

2

set_real_ip_from 10.1.10.0/24;

real_ip_header X-Forwarded-For;

在存在反向代理的情况下,如果直接获取REMOTE_ADDR,得到的是反向代理IP的值,从上面的配置也可以看出,在反向代理nginx的配置中将REMOTE_ADDR赋给了X-Real-IP,那么也是从X-Real-IP中来获取用户的IP,如下才是正确的获取用户IP的方式:

1

2

3

4

5

def get_ip(request):

try:

return request.META['HTTP_X_REAL_IP']

except KeyError:

return request.META.get('REMOTE_ADDR',None)

4 总结X-Forwarded-For可被用户伪造,不应该被信任;REMOTE_ADDR是使用“REMOTE_ADDR”机器的前一个建立tcp连接的机器的地址,是不可伪造的,在无代理时可以理解为用户的IP地址,有反向代理时,先将REMOTE_ADDR赋给X-Real-IP,最后可以从X-Real-IP中获取用户的IP。

参考文献:http://gong1208.iteye.com/blog/1559835http://devco.re/blog/2014/06/19/client-ip-detection/http://blog.pengqi.me/2013/04/20/remote-addr-and-x-forwarded-for/

文章分类: 安全分享

文章关键词: api接口, X-Forwarded-For REMOTE_ADDR 区别, 服务器获取IP, 用户伪造, 直接获取REMOTE_ADDR, 获取用户的IP, 获取用户真实IP, 认证绕过 转载请注明:“转自绿盟科技博客”: 原文链接.

相关推荐

阴阳师天邪鬼赤哪里多
365国际彩票下载

阴阳师天邪鬼赤哪里多

📅 06-30 👁️ 5575
荣耀畅玩5报价
office365E5无限续期

荣耀畅玩5报价

📅 07-03 👁️ 4280
速冻粽子煮多长时间(速冻粽子煮多长时间能煮熟)
office365E5无限续期

速冻粽子煮多长时间(速冻粽子煮多长时间能煮熟)

📅 07-09 👁️ 3308