1. 首先了解X-Forwarded-for头(简称:XFF)

  • 如需转载,请标明出处!!!
  • X-Forwarded-for:简称XFF,它代表客户端,也就是HTTP请求的真实IP,只有在通过了HTTP代理
    或者负载均衡器时才会添加该项。

1.1 产生背景

  • X-Forwarded-For是用来识别 “通过HTTP代理或负载均衡方式连接到WEB服务器的客户端” 最原始的ip地址的请求字段。
  • 服务端获取客户端ip地址的常用方法有两种:
    1. Remote Address
    2. X-Forwarded-for
  • 在java中,获取 客户端ip地址 最简单的方式就是 request.getRemoteAddr(),即第一种方式。
  • 这种方式可以直接获取到连接服务器的客户端ip(在中间没有代理的情况下,的确是最简单有效的方式)。
  • 但是当今的互联网web应用很少会将 应用服务器 直接 对外服务,有的甚至可能有多层代理。在有反向代理的情况下 直接使用 request.getRemoteAddr(),获取到的ip地址是Nginx(或其他的代理服务器的ip地址),而不是客户端的ip地址。
  • 这是因为:
    • HTTP协议是基于TCP协议的,由于request.getRemoteAddr()获取到的是TCP层直连的客户端ip,对于web应用服务器来说直接连接它的客户端实际上是Nginx,也就是说TCP层是拿不到真实的客户端的ip的。
  • 为了解决上面的问题,很多 HTTP代理 会在HTTP协议头中添加 X-Forwarded-for头,用于追踪请求来源最真实的ip地址。

1.2 XFF头格式:

  • 该HTTP头一般如下:
    • X-Forwarded-For:真实IP,代理1的IP,代理2的IP,代理3的IP, ...

1.3 XFF头ip追加原理:

  • 假设服务端收到的XFF信息为:X-Forwarded-For:IP0,IP1,IP2
    • 那么就说明:该请求成功通过了 三台 代理服务器:proxy1,proxy2,proxy3(在这里你可能会有疑问,为什么是3台代理服务器,请继续往下看)
  • 首先我们要知道,在XFF形成的过程中,代理服务器 每成功收到一个请求,就把 请求来源的IP地址 添加到右边。
    • 注意:
      1. 添加的是 请求来源的IP地址
      2. 添加ip地址 而不是 覆盖ip地址
  • 那么XFF头形成过程如下:
    • 在请求刚从client1中发出的时候,XFF是空的
    • 当请求 通过第一个代理 的时候,由于添加的是 请求来源的IP地址,所以该代理服务器会把 客户端/浏览器的IP地址(即最原始的IP地址)添加为client1;
    • 当请求 通过第二个代理 的时候,由于添加的是 请求来源的IP地址,所以该代理服务器会把 第一个代理的ip地址 追加到proxy1位置上;
    • 当请求 通过第三个代理 的时候,由于添加的是 请求来源的IP地址,所以该代理服务器会把 第二个代理的IP地址 追加到proxy2位置上;
    • 列表中之所以没有IP3,是因为proxy3直连服务器,proxy3会给XFF追加IP2,表示它是在帮proxy2转发请求,而IP3可以在服务端通过Remote Address 字段直接获得,不需要再往 XFF头 上继续追加了。
  • 从该过程便可以发现:
    • 由于添加的是 请求来源的IP地址,所以导致在 第一个代理处 添加 最原始的ip地址 ,在 第二个代理处添加 第一个代理的ip地址... 这样的错位的关系。

1.4 简单用法举例:

public String getClientIp(HttpServletRequest request) {
    String xff = request.getHeader("X-Forwarded-For");
  if (xff == null) {
    return request.getRemoteAddr();
  } else {
    return xff.contains(",") ? xff.split(",")[0] : xff;
  }
}
//另外,要让Nginx支持X-Forwarded-For头,需要配置:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  • 以上就是一种常用的获取客户端真实ip的方法:
    • 首先从HTTP头中获取X-Forwarded-For,如果X-Forwarded-For头存在就按逗号分隔取最左边第一个ip的地址,不存在就直接通过request.getRemoteAddr()获取ip地址
  • 注意:在使用X-Forwarded-For获取ip时,默认获取的是最左边的值(即client1)

2. 利用X-Forwarded-For伪造客户端ip漏洞:

  • 首先,我们要知道 Remote Address 是无法伪造的,因为它是直接从TCP连接信息中获取到的,而建立TCP连接需要三次握手。如果伪造了源ip,便无法建立TCP连接,更不会有后面的HTTP请求。
  • 但是,X-Forwarded-For头是可以伪造的。一般的客户端(如:浏览器)在发送请求的时候是没有X-Forwarded-For头的,当请求到达第一个代理服务器的时候,代理服务器会加上X-Forwarded-For请求头,并将值设为客户端的ip地址(也就是最左边的第一个值),后面如果还有多个代理,则会依次将ip追加到X-Forwarded-For都的最右边,最终当请求到达web应用服务器时,应用通过获取X-Forwarded-For头取左边第一个ip即为客户端真实ip
  • 但是如果客户端在发送请求时,就在请求头上带上一个伪造的X-Forwarded-For,由于后续的每层代理只会追加而不会覆盖ip,那么最终到达应用服务器时,获取的最左边第一个ip地址将会是客户端伪造的ip。也就是上面的java代码中getClientIp()方法获取的ip地址很有可能是伪造的ip地址。
  • 该漏洞如果没有加以限制,则会造成巨大的安全隐患
  • 可以利用工具Postman或burpsuite进行XFF攻击
内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/02SWD/p/13256385.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!