这篇文章资料来自于网络,对部分知识整理,这里只是记录一下,仅供参考。
1、ngx_http_realip_module模块
realip模块作用:当本机的nginx处于反向代理的后端时可以获取到用户的真实ip。可以让accesslog记录用户真实IP地址。
set_real_ip_from IP1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
- set_real_ip_from —— 设置反向代理服务器,即信任服务器IP
- real_ip_header X-Forwarded-For —— 用户真实IP存在X-Forwarded-For请求头中
- real_ip_recursive
- off —— 会将real_ip_header指定的HTTP头中的最后一个IP作为真实IP
- on —— 会将real_ip_header指定的HTTP头中的最后一个不是信任服务器的IP当成真实IP
2、X-Forwarded-For(XFF)和X-Real-IP、Remote Address
参考:
X-Forwarded-For的客户端IP坑_x-forwarded-for有没有必要设置-优快云博客
XFF位于HTTP请求头,是HTTP的扩展header,已经是事实上的标准。
X-Forwarded-For用于表示HTTP请求端真实IP。
格式如下:
X-Forwarded-For: client, proxy1, proxy2
nginx代理一般配置
关于X-Forwarded-For的配置,我看到了两种配置:
第一种:
proxy_set_header X-Forwarded-For "$http_x_forwarded_for, $remote_addr";
第二种:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#proxy_set_header X-Forwarded-For $remote_addr; #不合理,nginx之前的代理信息都被抹掉
说明:
- 1、X-Forwarded-For:最后一节是nginx追加上去的,但前面部分来源于nginx收到的请求头,这部分内容完全不可信。符合IP格式的才可以使用,否则容易引发XSS或者SQL注入漏洞。
- 2、Remote Address:HTTP协议没有IP的概念,Remote Address来自于TCP连接,表示与服务端建立TCP连接的设备IP,因此,Remote Address无法伪造。
- 3、X-Real-IP:HTTP代理用于表示与它产生TCP连接的设备IP,可能是其他代理,也可能是真正的请求端。
小结:
- 1、直接对外提供服务的web应用,在进行与安全有关的操作的时候,只能通过Remote Address获取IP,不能相信任何请求头。
- 2、使用nginx等web server进行反向代理的web应用,要使用X-Forwarded-For的最后一节或者 X-Real-IP来获取IP,同时还应该进行web应用直接对外提供服务。
- 3、在与安全无关的场景,可以通过 X-Forwarded-For的靠前位置获取IP,但是需要校验IP的合法性。
3、再介绍$proxy_add_x_forwarded_for、$http_x_forwarded_for
这两个的变量的值的区别,就在于,proxy_add_x_forwarded_for 比http_x_forwarded_for 多了一个$remote_addr的值。
但是只能获取到与服务器本身直连的上层请求,所以设置remoteaddr只能获取到与服务器本身直连的上层请求ip,所以设置remote_addr一般都是设置第一个代理上面
但是问题是,有时候是通过cdn访问过来的,那么后面web服务器获取到的,永远都是cdn 的ip 而非真是用户ip
那么这个时候就要用到x_forwarded_for了,这个变量的意思,其实就像是链路反追踪,从客户的真实ip为起点,穿过多层级的proxy ,最终到达web 服务器,都会记录下来,所以在获取用户真实ip的时候,
一般就可以设置成,proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 这样就能获取所有的代理ip 客户ip
在打印log 的时候,
$http_x_real_ip|$remote_addr
就是 用户的真是/2
4、引起问题的配置
最近遇到一个问题,这个问题主要是两个配置同时存在时,引起的:
配置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
配置:
real_ip_header X-Forwarded-For;
set_real_ip_from b.b.b.b;
real_ip_recursive on;
最总配置:
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
real_ip_header X-Forwarded-For;
set_real_ip_from b.b.b.b;
real_ip_recursive on;
}
5、现象
引用stackoverflow上一个例子:
I have a webapp under NGinx and another frontal load balancer, something like below (x.x.x.x = IP address):
Client(a.a.a.a) -> LB (b.b.b.b) -> NGX (c.c.c.c) -> WEBAPP (d.d.d.d)
Here is a snippet of my NGinx configuration:
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
real_ip_header X-Forwarded-For;
set_real_ip_from b.b.b.b;
real_ip_recursive on;
}
- The load balancer add
X-Forwarded-For
field with client IP
X-Forwarded-For
=a.a.a.a
- NGinx search for client real IP in
X-Forwarded-For
header by omiting LB IP (b.b.b.b
) and change$remote_addr
fromb.b.b.b
toa.a.a.a
soproxy_set_header X-Real-IP $remote_addr
become true (OK that's what I want !)
BUT, Nginx also completeX-Forwarded-For
header witha.a.a.a
IP instead ofb.b.b.b
- WEBAPP receive the following headers:
X-Forwarded-For
=a.a.a.a, a.a.a.a
X-Real-IP
=a.a.a.a
we want --->X-Forwarded-For
should bea.a.a.a, b.b.b.b
6、原因
this behaviour is caused by the real_ip_recursive on;
directive.
From the nginx realip docs:
If recursive search is enabled, an original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the request header field.
You have specified to trust b.b.b.b
(because of your set_real_ip_from b.b.b.b;
So what you would expect, i.e. a.a.a.a, b.b.b.b
will get replaced by a.a.a.a, a.a.a.a
.
7、解决方式
- The $proxy_add_x_forwarded_for is equal to
$http_x_forwarded_for,$remote_addr
, - and the
$remote_addr
variable will be changed whenhttp_realip_module
is used.
So you will not get the last proxy addr in that header. Changing the order of directives won't have an effect because nginx
configuration is declarative.
When http_realip_module is used, the $realip_remote_addr variable (nginx >= 1.9.7
) can be used as the original $remote_addr
. So you can set your X-Forwarded-For
header like this:
proxy_set_header X-Forwarded-For "$http_x_forwarded_for, $realip_remote_addr";
8、测试方式
# 设置可信IP,支持多个,也支持网段和IPv6
set_real_ip_from 192.168.1.0/24;
set_real_ip_from 192.168.2.1;
# 从X-Forwarded-For解析客户端真实IP,默认是X-Real-IP
real_ip_header X-Forwarded-For;
real_ip_recursive on;
server {
# 省略其他配置....
# nginx直接返回客户端IP到body
location /ip {
default_type text/plain;
# 返回remote_addr, x_real_ip, realip_remote_addr, x_forwarded_for
return 200 "remote_addr:$remote_addr\nx_real_ip:$http_x_real_ip\nrealip_remote_addr:$realip_remote_addr\nhttp_x_forwarded_for:$http_x_forwarded_for\n";
}
}
8、但是也不能真正获取到最终real IP
proxy_add_x_forwarded_for这个字段,会以追加的形式把多级代理的代理ip加起来,以逗号隔开,越靠近客户端的排在前面,越靠近最后服务端的排在后面,但存在被伪造的可能性,比如使用以下命令测试:
curl http://localhost -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2'
日志打印出来的ip可能会是"1.1.1.1, 127.0.0.1, 192.168.100.101, 192.168.100.102",其中1.1.1.1排在最前面,但他不是最真实的客户端ip
对于伪造代ip的处理方法
只需要在第一个代理设置proxy_set_header X-Real-IP $remote_addr;就好了,然后再应用端直接引用$http_x_real_ip就行。
/9、参考
https://stackoverflow.com/questions/29279084/nginx-proxy-add-x-forwarded-for-and-real-ip-header https://serverfault.com/questions/314574/nginx-real-ip-header-and-x-forwarded-for-seems-wrong