Nginx转发获取客户端真实IP
前言
反向代理相信大家都不陌生,假设要在真实服务器需要做一些区域分析,频率限制等等,就需要拿到真实的请求用户的IP。在4层转发里面,可以通过TOA的方式。在7层转发里面,可以通过头部信息获取(Nginx也可以转发$remote_addr,下次再说)。
准备环境
如下图所示,在测试环境模拟一个Http请求经过反向代理服务器的过程,观察一下获取真实IP的情况。
用的是Nginx作为反向代理服务器,后端用php展示获得的头部信息。一般来说用X-Forwarded-For
,X-Real-IP
这两个头部信息和$remote_addr
变量来获取真实IP。
具体的配置如下:
反向代理1
server {
listen 192.168.50.60:80;
server_name www.bcsama.top;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Real-IP $remote_addr;
location / {
proxy_pass http://192.168.50.70;
}
}
反向代理2
server {
listen 192.168.50.70:80;
server_name www.bcsama.top;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Real-IP $remote_addr;
location / {
proxy_pass http://192.168.50.146;
}
}
真实服务器
server {
listen 80;
server_name www.bcsama.top;
# real_ip_header X-Forwarded-For;
# set_real_ip_from 192.168.50.0/24;
# real_ip_recursive on;
location / {
root /data;
index real_IP.php;
}
location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
}
real_IP.php
<?php
printf('HTTP_X_FORWARDED_FOR -> ');
printf($_SERVER['HTTP_X_FORWARDED_FOR']);
echo "<pre>";
printf('HTTP_X_REAL_IP -> ');
printf($_SERVER['HTTP_X_REAL_IP']);
echo "<pre>";
printf('REMOTE_ADDR -> ');
printf($_SERVER['REMOTE_ADDR']);
echo "<pre>";
?>
测试过程
1、默认情况下,两台反向代理服务器都不配置相关的头部信息,此时后端获取到的都是反向代理服务器2的IP
2、反向代理服务器1添加
X-Forwarded-For
和X-Real-IP
头部 ,真实服务器得到的信息如下3、在过程2的状态下,反向代理服务器2仅添加
X-Forwarded-For
头部,真实服务器得到的信息如下4、在过程3的状态下,反向代理服务器2继续添加
X-Real-IP
头部,真实服务器得到的信息如下5、在过程3的状态下,真实服务器继续添加如下配置,真实服务器得到的信息如下
real_ip_header X-Forwarded-For; set_real_ip_from 192.168.50.0/24; real_ip_recursive off;
6、在过程5的状态下,真实服务器把
real_ip_recursive off
改成real_ip_recursive on
真实服务器得到的信息如下测试说明
1) 在默认情况下(反代都没有添加头部信息),真实服务器获取客户IP,$remote_addr
为最后一台反向代理服务器的IP。此时X-Forwarded-For
为空值。
2) 对比过程2和3可知,X-Forwarded-For
为数组,后面的反代服务器会把前面的反代服务器IP添加到里面,中间是逗号分隔。
3) 对比过程3和4可知,$remote_addr
会被后面的反代服务器吧IP换掉。
4) 由过程5和6可知,由real_ip_header
指定头部可以让$remote_addr
获取到真实IP。set_real_ip_from
表示哪些为反向代理的IP,可以用IP(段)表示。real_ip_recursive
表示是否从后递归解析,用于排除set_real_ip_from
里面的IP(段)。
头部与变量说明
$remote_addr
变量,指请求的IP,就是tcp握手那个$proxy_add_x_forwarded_for
变量,指客户端请求头“X-Forwared-For”这个字段,其中附加了$ remote_addr
变量,以逗号分隔。 如果客户端请求标头中不存在“X-Forwarded-For”字段,则$ proxy_add_x_forwarded_for
变量等于$ remote_addr
变量。用最简单的方法来表示就是:$proxy_add_x_forwarded_for
=$http_x_forwarded_for
+ ‘,‘+$ remote_addr
$http_x_forwarded_for
变量,指客户端请求头中的 “X-Forwared-For” 这个字段。$http_x_real_ip
变量,指客户端请求头中的 “X-Real-IP” 这个字段。X-Real-IP
就是一个自定义的变量名字,习惯性用法。
伪造头部
既然我们使用头部信息来获取真实IP,那人为伪造头部岂不是让人钻了漏洞? 接下来验证一下。
在刚刚的过程6的环境中,我们在客户端使用curl命令伪造头部:
# curl -H "Host: www.bcsama.top" -H "X-Forwarded-For: 1.1.1.1" -H "X-Real-IP: 2.2.2.2" http://192.168.50.60
HTTP_X_FORWARDED_FOR -> 1.1.1.1, 192.168.20.237, 192.168.50.60
HTTP_X_REAL_IP -> 192.168.20.237
REMOTE_ADDR -> 192.168.20.237
p.s. 用curl命令得到的结果, X-Forwarded-For 数组顺序为从左到右。
由结果可知,伪造
X-Forwarded-For
的确生效,插在数组的第一个值,但X-Real-IP
不成功。假设光取X-Forwarded-For
的第一个值,那就是错的。而过程6里面real_ip_recursive
的值为 on,排除了反向代理服务器IP之后,取最后一个IP为客户端IP,因此不影响真实服务器获取真实客户端IP。由于
$remote_addr
不容易伪造(考虑TCP握手甚至是防火墙的挑战),我们可以把第一台反向代理服务器的头部修改成:proxy_set_header X-Forwarded-For $remote_addr;
。
小结
到底是采用获取请求头部的方式还是使用realip模块的方式,请根据自身的业务需求来判断。
无论采用哪种方式,若有多级反向代理,请在第一级反向代理机器上设置头部。之后的反代服务器请根据业务需求来添加(注意两个变量是数组和非数组的区别)。
由于
X-Forwarded-For
太容易伪造,请根据自身业务修改判断逻辑。
最后更新于 2019-04-26 09:31:41 并被添加「」标签,已有 33934 位童鞋阅读过。
本站使用「署名 4.0 国际」创作共享协议,可自由转载、引用,但需署名作者且注明文章出处