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

举报
人生苦短,我用Python 发表于 2017/11/17 01:50:05 2017/11/17
【摘要】 如何获取用户的IP,这个需求简直是太常见了,像登录入口,注册入口,投票,日志记录,api接口中判断同一个ip单位时间内的请求数,可是怎么去获取用户的真实IP呢?网上的代码很多,好多人直接拿来就用,却没有想到带来了很大的安全问题。1 代码示例12345678910<?phpif(!empty($_SERVER['HTTP_CLIENT_IP'])){ $myip = $_SERVER['HTTP

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

1

2

3

4

5

6

7

8

9

10

<?php

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

<font style="font-family:Arial,Helvetica,Sans Serif;font-size: 24pt;" color="#0066CC"><b>您的IP地址: 127.0.0.111111</b></font>

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/1559835
http://devco.re/blog/2014/06/19/client-ip-detection/
http://blog.pengqi.me/2013/04/20/remote-addr-and-x-forwarded-for/


文章分类: 安全分享 




【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。