WEB应用取访问者IP时的一个小坑

WEB应用取访问者IP时有一个小坑,以下均以Django为例,其他语言或框架同理。

百度“Django 取ip”,找到几种不同的代码,但其本质相同,取其中一种具有代表性的摘录如下:

    if 'HTTP_X_FORWARDED_FOR' in request.META:
          client_ip = request.META['HTTP_X_FORWARDED_FOR']  
          client_ip = client_ip.split(",")[0]  
    else:
          client_ip = request.META['REMOTE_ADDR']  

这个代码来自《python django 获取用户IP地址的方法》
看上去很完美,当存在“HTTP_X_FORWARDED_FOR”,即HTTP头中有“X-Forwarded-For”字段时,取该字段的值,并对其值按“,”分割,取分割出结果的第一项作为用户IP。

“X-Forwarded-For”字段本应是透明HTTP代理添加的,以告诉服务器,是在为谁转发,当有多个代理时,依次在此字段后追加IP地址,并用英文逗号和空格隔开,如用户IP是1.1.1.1,第一个代理的IP是2.2.2.2,第二个代理的IP是3.3.3.3,若两个代理都是透明代理,则服务器收到的HTTP请求的“X-Forwarded-For”字段的值应当是(暂不考虑反向代理):

    X-Forwarded-For: 1.1.1.1, 2.2.2.2

1.1.1.1是第一个代理添加的,2.2.2.2是第二个代理添加的,它在为第一个代理转发数据。服务器和第二个代理建立TCP连接,故服务器也知道第二个代理的IP是3.3.3.3。

若所有人都是诚实的,那么这段取IP地址的代码便没有问题。但并不是所有人都是诚实的,HTTP头部字段可以伪造。用户可能直接连接了服务器,但它也可以自己填写“X-Forwarded-For”字段的值为“3.3.3.3, 2.2.2.2”,这样,服务器便以为用户IP是3.3.3.3了,下一次,用户可以填写“X-Forwarded-For”字段的值为“4.4.4.4”,这样服务器便又以为用户IP是4.4.4.4了。

更有甚者,用户还可以不填写“X-Forwarded-For”字段的值为一个IP地址,而是一个sql注入语句,这就造成了HTTP头注入,当然,在Django中不存在这个问题,但在php等语言中这可能会是很严重的问题。

在所有IP地址中,只有最终与服务器建立TCP连接的IP地址是可信的,其他IP地址均可伪造,故出于安全的考虑应将与服务器建立TCP连接的IP地址作为用户IP,就算它是HTTP代理。若非要取“X-Forwarded-For”字段的值,则应注意检查IP地址格式是否正确,防止注入等攻击。

从攻击者的角度考虑,若是遇到某个网站,当我使用透明代理(也就是会填写“X-Forwarded-For”字段的代理)时,仍能得知我的真正IP,那么我应该感到高兴,因为这说明该网站是从“X-Forwarded-For”字段读IP地址的,我只需要伪造该字段,便可以不停“更换”自己的IP地址了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注