X-Forwarded-For的一些理解
X-Forwarded-For的⼀些理解
X-Forwarded-For 是⼀个 HTTP 扩展头部,主要是为了让 Web 服务器获取访问⽤户的真实 IP 地址(其实这个真实未必是真实的,后⾯会说到)。
那为什么 Web 服务器只有通过 X-Forwarded-For 头才能获取真实的 IP?
这⾥⽤ PHP 语⾔来说明,不明⽩原理的开发者为了获取客户 IP,会使⽤ $_SERVER['REMOTE_ADDR'] 变量,这个服务器变量表⽰和 Web 服务器握⼿的 IP 是什么(这个不能伪造)。
但是很多⽤户都通过代理来访问服务器的,那么假如使⽤该全局变量,PHP获取到的 IP 就是代理服务器的 IP(不是⽤户的)。
可能很多⼈看的晕乎乎的,那么看看⼀个请求可能经过的路径:
客户端=>(正向代理=>透明代理=>服务器反向代理=>)Web服务器
其中正向代理、透明代理、服务器反向代理这三个环节并不⼀定存在。
什么是正向代理呢,很多企业会在⾃⼰的出⼝⽹关上设置代理(主要是为了加速和节省流量)。
透明代理可能是⽤户⾃⼰设置的代理(⽐如为了FQ,这样也绕开了公司的正向代理)。
服务器反向代理是部署在 Web 服务器前⾯的,主要原因是为了负载均衡和安全考虑。
现在假设⼏种情况:
假如客户端直接连接 Web 服务器(假设 Web 服务器有公⽹地址),则 $_SERVER['REMOTE_ADDR'] 获取到的是客户端的真实 IP 。
假设 Web 服务器前部署了反向代理(⽐如 Nginx),则 $_SERVER['REMOTE_ADDR'] 获取到的是反向代理设备的 IP(Nginx)。
假设客户端通过正向代理直接连接 Web 服务器(假设 Web 服务器有公⽹地址),则 $_SERVER['REMOTE_ADDR'] 获取到的正向代理设备的 IP 。
其实这⾥的知识点很多,记住⼀点就⾏了,$_SERVER['REMOTE_ADDR'] 获取到的 IP 是 Web 服务器 TCP 连接的 IP(这个不能伪造,⼀般 Web 服务器也不会修改这个头)。
X-Forwarded-For
从上⾯⼤家也看出来了,因为有了各种代理,才会导致REMOTE_ADDR 这个全局变量产⽣了⼀定的歧义,为了让Web 服务器获取到真实的客户端IP,X-Forwarded-For 出现了,这个协议头也是由 Squid 起草的(Squid 应该是最早的代理软件之⼀)。
这个协议头的格式:
X-Forwarded-For: client, proxy1, proxy2
client 表⽰⽤户的真实 IP,每经过⼀次代理服务器,代理服务器会在这个头增加⽤户的 IP(有点拗⼝)。
注意最后⼀个代理服务器请求Web 服务器的时候是不会将⾃⼰的IP 附加到X-Forwarded-For 头上的,最后⼀个代理服务器的IP 地址应该通过$_SERVER['REMOTE_ADDR']获取。
举个例⼦:
⽤户的 IP 为(A),分别经过两个代理服务器(B,C),最后到达 Web 服务器,那么Web 服务器接收到的 X-Forwarded-For 就是 A,B。
那么 PHP 如何获取真实客户端 IP 呢?
$ip = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? trim($_SERVER['HTTP_X_FORWARDED_FOR']) : '';
if (!$ip) {
$ip = isset($_SERVER['REMOTE_ADDR']) ? trim($_SERVER['REMOTE_ADDR']) : '';
}
$a = explode('|', str_replace(',', '|', $ip));
$ip = trim($a[0]);
这⾥预先说明下,假设这两个代理服务器都是好的代理服务器,没有伪造 HTTP_X_FORWARDED_FOR。
配置反向代理
上⾯⼀直在说代理,⼤家可能觉得这到底有啥⽤?不同类型的代理有不同的⽬的,对于正向代理来说主要是为了加速并且让局域⽹的⽤户有⼀个真实的IP 地址,⽽透明代理则主要是为了⼀些其他的⽬的
(⽐如就是不想让别⼈知道我的 IP),⽽反向代理主要是企业内部安全和负载均衡考虑,这⾥主要说下如何配置反向代理。
现在只要是具备⼀定规模的⽹站(Web 服务器⼤于  1 台),为了安全和负载均衡考虑都会在Web 服务器前⾯部署反向代理,反向代理有HAproxy,Nginx,Apache 等等。
如何设置代理服务器这⾥通过 Nginx 来部署反向代理:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
简单的解释下:
X-Forwarded-For 表⽰ Nginx 接收到的头,原样的转发过来(假如不转发,Web 服务器就不能获取这个头)。
X-Real-IP,这是⼀个内部协议头(就是反向代理服务器和 Web 服务器约定的),这个头表⽰连接反向代理服务器的 IP 地址(这个地址不能伪造),其实个⼈觉得为了让 PHP 代码保持⽆⼆义性,不应该这样设置,可以修改为proxy_set_header REMOTE_ADDR $remote_addr;
Apache WEB 服务器的 Access ⽇志如何获取 X-Forwarded-For 头
其实写这篇⽂章主要是因为⾃⼰在 Apache Web 服务器上获取不到 X-Forwarded-For(上层的负载均衡设备确定传递了),搜索了下(在 Apache 官⽅⽂档并没有到解决⽅案),解决如下:
LogFormat "%{X-Forwarded-For}i %a %h %A %l %u %t \"%r\" %>s %b \"%{Referer}i\"
\"%{User-Agent}i\"" combined
X-Forwarded-For 安全性
那么很多同学会说,通过 X-Forwarded-For 就能获取到⽤户的真实 IP,是不是万事⼤吉了,对于 Web 服务器来说,安全有两个纬度,第⼀个纬度是 REMOTE_ADDR 这个头,这个头不能伪造。第⼆个纬度就是 X-Forwarded-For,但是这个头是可以伪造的。
那么谁在伪造呢?,我们分别看下:
正向代理⼀般是公司加速使⽤的,假如没有特殊的⽬的,不应该传递 X-Forwarded-For 头,因为它的上层连接是内部 IP,不应该暴露出去,当然它也可以透明的传递这个头的值(⽽这个值⽤户可以伪造)。
透明代理,这个可能是⽤户⾃⼰搭建的(⽐如FQ),⽽且在⼀个⽤户的请求中,可能有多个透明代理,这时候透明代理就抓瞎了,为了让⾃⼰尽量的正确,也会透明的传递这个头的值(⽽这个值⽤户可以伪造),当然⼀些不法企业或者⼈员,为了⼀些⽬的,会改下这个头的值(⽐如来⾃世界各地的 IP 地址)。
反向代理,Web 服务器前的反向代理服务器是不会伪造的(同⼀个公司的),⼀般会原样传递这个头的值。
那么对应⽤程序来说,既然这个值不能完全相信,该怎么办呢?这取决于应⽤的性质:
假如提供的服务可能就是⼀些⾮机密服务,也不需要知道⽤户的真实 IP,那么建议应⽤程序或者 Web 服务器对 REMOTE_ADDR 做⼀些限制,⽐如进⾏限速等等,也可以放⾏⼀些⽩名单的代理 IP,但是这些⽩名单 IP 就太难衡量了。
假设你的服务很重要,⽐如抽奖(⼀个 IP 只能⼀次抽奖),这时候你可能想通过 X-Forwarded-For 来
获取⽤户的真实 IP(假如使⽤ REMOTE_ADDR 则会误杀⼀⽚),但是由于 X-Forwarded-For 可能会伪造,所以其实并没有什么好的办法,只能在应⽤层进⾏处理了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。