Nginx $proxy_add_x_forwarded_for and $real_ip_header, X-Forwarded-For proxy IP changes

这篇文章资料来自于网络,对部分知识整理,这里只是记录一下,仅供参考。

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;
}
  1. The load balancer add X-Forwarded-For field with client IP
    X-Forwarded-For = a.a.a.a
  2. NGinx search for client real IP in X-Forwarded-For header by omiting LB IP (b.b.b.b) and change $remote_addr from b.b.b.b to a.a.a.a so proxy_set_header X-Real-IP $remote_addr become true (OK that's what I want !)
    BUT, Nginx also complete X-Forwarded-For header with a.a.a.a IP instead of b.b.b.b
  3. 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 be a.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、解决方式

  1. The $proxy_add_x_forwarded_for is equal to $http_x_forwarded_for,$remote_addr,
  2. and the $remote_addr variable will be changed when http_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、参考

Module ngx_http_realip_module

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

nginx获取请求真实IP-腾讯云开发者社区-腾讯云

腾讯云CLB负载均衡后端nginx配置获取客户端真实IP - IT打杂分享

世界地图矢量数据可以通过多种网站进行下载。以下是一些提供免费下载世界地图矢量数据的网站: 1. Open Street Map (https://www.openstreetmap.org/): 这个网站可以根据输入的经纬度或手动选定范围来导出目标区域的矢量图。导出的数据格式为osm格式,但只支持矩形范围的地图下载。 2. Geofabrik (http://download.geofabrik.de/): Geofabrik提供按洲际和国家快速下载全国范围的地图数据数据格式支持shape文件格式,包含多个独立图层,如道路、建筑、水域、交通、土地利用分类、自然景观等。数据每天更新一次。 3. bbbike (https://download.bbbike.org/osm/): bbbike提供全球主要的200多个城市的地图数据下载,也可以按照bbox进行下载。该网站还提供全球数据数据格式种类齐全,包括geojson、shp等。 4. GADM (https://gadm.org/index.html): GADM提供按国家或全球下载地图数据的服务。该网站提供多种格式的数据下载。 5. L7 AntV (https://l7.antv.antgroup.com/custom/tools/worldmap): L7 AntV是一个提供标准世界地图矢量数据免费下载的网站。支持多种数据格式下载,包括GeoJSON、KML、JSON、TopJSON、CSV和高清SVG格式等。可以下载中国省、市、县的矢量边界和世界各个国家的矢量边界数据。 以上这些网站都提供了世界地图矢量数据免费下载服务,你可以根据自己的需求选择合适的网站进行下载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值