在 http 头中有一个字段:X-Forwarded-For,该字段是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。

假设一个场景,你的网站使用了 CDN 加速, CDN 会隐藏掉你源站的真实 IP, 当请求到达时,会先请求到 CDN 边缘节点,而后转发到源站,此时你可以把 CDN 当做是一个代理服务器,事实上,它也确实是一个代理服务器。

字段介绍

X-Forwarded-For 请求头格式非常简单,就这样:

X-Forwarded-For: client, proxy1, proxy2

其内容由英文逗号 + 空格隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。

如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,服务端最终会收到以下信息:

X-Forwarded-For: IP0, IP1, IP2

伪造 IP

现在很多的网站都会使用代理或 CDN(CDN 本质上也是代理服务器),当启用了代理服务器,后端服务器就无法直接获取客户端的真实 IP 地址了,比如 又拍云 CDN 回源的时候默认传到源站的是 IP 是中转节点的 IP,又拍云会提供2个http 字段 X-Real-IP 和 X-Forwarded-For 来传递真实的客户的 IP,这样即使走 CDN 源站也能通过这2个字段来获取真实的 IP 地址,详见文档

但是 X-Forwarded-For 是可以非常轻易的伪造的,和伪造 referer 一样简单。

比如使用又拍云的 CDN 加速后,如果想拿到真实 IP 只能用又拍提供的获取真实 IP 文档所描述的内容来获取,但是客户的这样请求:

curl -X GET https://www.fangwenjun.com/post/54079.html -H "X-Forwarded-For:1.1.1.1" 

服务端日志格式如下:

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                                     '"$status" $body_bytes_sent "$http_referer" '
                                     '"$http_user_agent" "$http_x_forwarded_for" '
                                     '"$gzip_ratio" $request_time $bytes_sent $request_length';

获得的日志内容是

183.131.24.75 - - [13/Dec/2017:14:42:35 +0800] "GET /post/54079.html HTTP/1.1" "200" 28328 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" "1.1.1.1" "-" 0.000 28716 412 

其中 183.131.24.75 是 CDN 回源的 IP ,也就是$remote_addr 这个 IP 是无法伪造的,而我们可以看到 1.1.1.1 对应的就是$http_x_forwarded_for 这个 IP 实际是伪造的。

所以,仅仅通过 X-Forwarded-For 来判断真实 IP 是不可靠的。

那么,利用这个漏洞能干嘛?

现在很多网站登录的时候都会针对同一 IP 源进行限制访问次数,或者投票系统,如果以 X-Forwarded-For 作为判断真实 IP 为依据,则很轻易的就会被绕过限制进行暴力破解和刷票。

via.https://awen.me/post/16558.html