X-Forwarded-For 拿到的就是真实 IP 吗?

1.故事

在这个小节开始前,我先讲一个开发中的小故事,可以加深一下大家对这个字段的理解。

前段时间要做一个和风控相关的需求,需要拿到用户的 IP,开发后灰度了一小部分用户,测试发现后台日志里灰度的用户 IP 全是异常的,哪有这么巧的事情。随后测试发过来几个异常 IP:

10.148.2.122
10.135.2.38
10.149.12.33
...

一看 IP 特征我就明白了,这几个 IP 都是 10 开头的,属于 A 类 IP 的私有 IP 范围(10.0.0.0-10.255.255.255),后端拿到的肯定是代理服务器的 IP,而不是用户的真实 IP。

2.原理


image-20200524154345598

现在有些规模的网站基本都不是单点 Server 了,为了应对更高的流量和更灵活的架构,应用服务一般都是隐藏在代理服务器之后的,比如说 Nginx。

加入接入层后,我们就能比较容易的实现多台服务器的负载均衡和服务升级,当然还有其他的好处,比如说更好的内容缓存和安全防护,不过这些不是本文的重点就不展开了。

网站加入代理服务器后,除了上面的几个优点,同时引入了一些新的问题。比如说之前的单点 Server,服务器是可以直接拿到用户的 IP 的,加入代理层后,如上图所示,(应用)原始服务器拿到的是代理服务器的 IP,我前面讲的故事的问题就出在这里。

Web 开发这么成熟的领域,肯定是有现成的解决办法的,那就是 X-Forwarded-For 请求头。

X-Forwarded-For 是一个事实标准,虽然没有写入 HTTP RFC 规范里,从普及程度上看其实可以算 HTTP 规范了。

这个标准是这样定义的,每次代理服务器转发请求到下一个服务器时,要把代理服务器的 IP 写入 X-Forwarded-For 中,这样在最末端的应用服务收到请求时,就会得到一个 IP 列表:

X-Forwarded-For: client, proxy1, proxy2

因为 IP 是一个一个依次 push 进去的,那么第一个 IP 就是用户的真实 IP,取来用就好了。

但是,事实有这么简单吗?

3.攻击

从安全的角度上考虑,整个系统最不安全的就是人,用户端都是最好攻破最好伪造的。有些用户就开始钻协议的漏洞:X-Forwarded-For 是代理服务器添加的,如果我一开始请求的 Header 头里就加了 X-Forwarded-For ,不就骗过服务器了吗?

1. 首先从客户端发出请求,带有 X-Forwarded-For 请求头,里面写一个伪造的 IP:

X-Forwarded-For: fakeIP

2. 服务端第一层代理服务收到请求,发现已经有 X-Forwarded-For,误把这个请求当成代理服务器,于是向这个字段追加了客户端的真实 IP:

X-Forwarded-For: fakeIP, client

3. 经过几层代理后,最终的服务器拿到的 Header 是这样的:

X-Forwarded-For: fakeIP, client, proxy1, proxy2

要是按照取 X-Forwarded-For 第一个 IP 的思路,你就着了攻击者的道了,你拿到的是 fakeIP,而不是 client IP。

4.破招

服务端如何破招?上面三个步骤:

  • 第一步是客户端造假,服务器无法介入
  • 第二步是代理服务器,可控,可防范
  • 第三步是应用服务器,可控,可防范

第二步的破解我拿 Nginx 服务器举例。

我们在最外层的 Nginx 上,对 X-Forwarded-For 的配置如下:

proxy_set_header X-Forwarded-For $remote_addr;

什么意思呢?就是最外层代理服务器不信任客户端的 X-Forwarded-For 输入,直接覆盖,而不是追加

非最外层的 Nginx 服务器,我们配置:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

$proxy_add_x_forwarded_for 就是追加 IP 的意思。通过这招,就可以破解用户端的伪造办法。

第三步的破解思路也很容易,正常思路我们是取X-Forwarded-For 最左侧的 IP,这次我们反其道而行之,从右边数,减去代理服务器的数目,那么剩下的 IP 里,最右边的就是真实 IP。

X-Forwarded-For: fakeIP, client, proxy1, proxy2

比如说我们已知代理服务有两层,从右向左数,把 proxy1proxy2 去掉,剩下的 IP 列表最右边的就是真实 IP。

相关思路和代码实现可参考 Egg.js 前置代理模式

5.一句话总结总结

通过 X-Forwarded-For 获取用户真实 IP 时,最好不要取第一个 IP,以防止用户伪造 IP。

文章推荐

下面我要推荐我的几篇文章:


最后推荐一下我的个人公众号:「卤蛋实验室」,平时会分享一些前端技术和数据分析的内容,大家感兴趣的话可以关注一波:

你确信 X-Forwarded-For 拿到的就是用户真实 IP 吗?的更多相关文章

  1. getRemoteAddr()和getRemoteHost() 区别

    System.out.println("request.getRemoteAddr(): " + request.getRemoteAddr()); System.out.prin ...

  2. JAVA获取客户端IP地址

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  3. 多级反向代理下,Java获取请求客户端的真实IP地址多中方法整合

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  4. servlet request getHeader(“x-forwarded-for”) 获取真实IP

    request方法客户端IP: request.getRemoteAddr() 输出:192.168.0.106 客户端主机名:request.getRemoteHost()输出:abc reques ...

  5. JAVA_用Java来获取访问者真实的IP地址

    在jsp里,获取客户端的ip地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  6. Java获取IP地址:request.getRemoteAddr()注意

        在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr() ,这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户 ...

  7. Java获取客户端真实IP地址的两种方法

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  8. 关于httpservletrequest的获取真实的ip

    via 值为: 下面是一些DemoWTP/1.1 GDSZ-PS-GW010-WAP05.gd.chinamobile.com (Nokia WAP Gateway 4.0 CD3/ECD13_C/N ...

  9. CXF详细介绍

    首先介绍下CXF的拦截器:简单地说,CXF使用流水线型(或者说总线型)处理机制,它的核心是一个Bus.一个客户端的请求或者一个对客户端桩代码的调用被组织成为一个Message.同时,所有的CXF功能都 ...

随机推荐

  1. codeforce 227E 矩阵快速幂求斐波那契+N个连续数求最大公约数+斐波那契数列的性质

    E. Anniversary time limit per test2 seconds memory limit per test256 megabytes inputstandard input o ...

  2. python(os 模块)

    1.os.name 输出字符串指示正在使用的平台.如果是window 则用'nt'表示,对于Linux/Unix用户,它是'posix' import os print(os.name) #结果如下 ...

  3. VMware的安装与部署Linux系统

            首先我们需要准备好我们将会用到的东西:VMware12.RHEL7.0         网址我就不放了,大家自行百度哟. 一.安装VMware         我们需要安装VMware ...

  4. 2019国防科大校赛 B Escape LouvreⅡ

    https://ac.nowcoder.com/acm/contest/878/B 这个题目是一个网络流,但是建图却没有那么好建,首先我们都会把每一个人与源点相连,每一个洞口和汇点相连. 然后人和洞口 ...

  5. BOM基础

    BOM基础 打开窗口 window.open('about:blank','_blank') 第一个参数是打开哪一个口,第二个参数是在哪里打开窗口. 关闭窗口 window.close() windo ...

  6. BIO、NIO、AIO的形象比喻

    BIO (Blocking I/O):同步阻塞I/O模式. NIO (New I/O):同步非阻塞模式. AIO (Asynchronous I/O):异步非阻塞I/O模型. 先看阻塞和非阻塞的区别, ...

  7. CF#214 C. Dima and Salad 01背包变形

    C. Dima and Salad 题意 有n种水果,第i个水果有一个美味度ai和能量值bi,现在要选择部分水果做沙拉,假如此时选择了m个水果,要保证\(\frac{\sum_{i=1}^ma_i}{ ...

  8. Spring Boot 入门(十三):集成Hasor的Dataway模块,干掉后台,自动配置接口

    终于出湖北了,封闭2个月,家里没电脑,感觉好久没自主撸代码啊啊啊啊啊啊啊啊啊啊啊啊啊. 连接上篇文章Spring Boot 入门(十二):报表导出,对比poi.jxl和esayExcel的效率,继续从 ...

  9. PHP对象基础

    class demo1 { public function test1(){ echo '这是一个公有方法,可以随意调用!' } protected function test2(){ $this-& ...

  10. 2018-06-19 js DOM对象

    DOM对象: Doucument Object Model即文档对象 DOM对象的操作: 1.找元素 返回元素对象: var obj=document.getElementById();//通过Id查 ...