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地址了。