Nginx透过代理获取真实客户端IP
本系列中的故事纯属虚构,如有雷同实属巧合
小B是’柒’公司的安全攻城狮,为了完成任务小B开始做起了调研(欲知背景如何,且听下回分说)。
首先小B弄明白了’柒’公司的应用系统架构是:Client --> CDN --> SLB --> Server
。
发现在应用服务器上Nginx日志中采集的关于定位用户身份信息的IP维度数据不准确。不准确的原因是:因为在应用服务器中Nginx使用XFF
与remote_addr
字段采集客户IP,XFF字段很好被攻击者伪造,而remote_addr
字段一般采集都是直连时的IP,在经过多层代理、网关等设备时,更容易导致后端服务器获取的客户端IP不真实。
于是乎小B开始研究"Nginx如何获取客户端真实IP",下文是一些研究总结:
默认设置获取到不真实的IP
代理与服务器配置
- Nginx_Server配置:
vim /opt/nginx/conf/nginx.conf
,服务器不作任何修改
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;}}
- Proxy_1配置:
vim /opt/nginx/conf/nginx.conf
,配置代理转发
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.99;}}
- Proxy_2配置:
vim /opt/nginx/conf/nginx.conf
,配置代理转发
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.100;}}
正常访问的日志情况
此时我们的网络架构为:
# 客户端使用命令访问
curl -XGET "http://10.10.10.98"
- Nginx_Server日志:
10.10.10.99 - - [11/Dec/2019:09:04:42 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
- Proxy_1日志:
10.10.10.1 - - [11/Dec/2019:09:04:43 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
- Proxy_2日志:
10.10.10.98 - - [11/Dec/2019:09:04:42 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
此时在Nginx_Server中无法获取客户端真实IP。
伪造XFF的日志情况
此时我们的网络架构为:
# 客户端访问时使用XFF
curl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
- Nginx_Server日志:
10.10.10.99 - - [11/Dec/2019:09:07:33 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.5"
- Proxy_1日志:
10.10.10.1 - - [11/Dec/2019:09:07:32 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "10.10.10.5"
- Proxy_2日志:
10.10.10.98 - - [11/Dec/2019:09:07:32 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.5"
此时在Nginx_Server中无法获取客户端真实IP。
使用X-Forwarded-For+Nginx readip模块获取
使用realip
模块可以获取客户端真实IP,该方法也是目前使用最多最有效的方法。
查看nginx的编译参数:/opt/nginx/sbin/nginx -V
(如果是yum安装Nginx,则该模块是默认安装的,我这里是使用编译安装的)
set_real_ip_from
:表示从何处获取真实IP,只认可自己信赖的IP,可以是网段,也可以设置多个。real_ip_header
:表示从哪个header属性中获取真实IP。real_ip_recursive
:递归检索真实IP,如果从X-Forwarded-For中获取,则需要递归检索;如果中X-Real-IP中获取,无需递归。
代理与服务器配置
- Nginx_Server配置:
vim /opt/nginx/conf/nginx.conf
,主要是在Server中新增代理服务器信息。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;# 注意这里的key value之间使用Tab而不要使用单个空格set_real_ip_from 10.10.10.98;set_real_ip_from 10.10.10.99;real_ip_header X-Forwarded-For;real_ip_recursive on;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;}}
检查配置文件是否正确:/opt/nginx/sbin/nginx -t
,然后重新加载配置文件:/opt/nginx/sbin/nginx -s reload
- Proxy_1配置:
vim /opt/nginx/conf/nginx.conf
,设置代理并且设置XFF字段信息。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.99;proxy_set_header Host $http_host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}}
- Proxy_2配置:
vim /opt/nginx/conf/nginx.conf
,设置代理并且设置XFF字段信息。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.100;proxy_set_header Host $http_host;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}}
正常访问的日志情况
此时我们的网络架构为:
# 客户端使用命令访问
curl -XGET "http://10.10.10.98"
- Nginx_Server日志:
10.10.10.1 - - [09/Dec/2019:09:19:21 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.1, 10.10.10.98"
- Proxy_1日志:
10.10.10.1 - - [09/Dec/2019:09:19:21 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
- Proxy_2日志:
10.10.10.98 - - [09/Dec/2019:09:19:21 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.1"
此时在Nginx_Server中remote_addr就是用户的真实IP。
伪造XFF的日志情况
此时我们的网络架构为:
# 客户端访问时使用XFF
curl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
- Nginx_Server日志:
10.10.10.1 - - [09/Dec/2019:09:20:03 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.5, 10.10.10.1, 10.10.10.98"
- Proxy_1日志:
10.10.10.1 - - [09/Dec/2019:09:20:03 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "10.10.10.5"
- Proxy_2日志:
10.10.10.98 - - [09/Dec/2019:09:20:03 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.5, 10.10.10.1"
此时在Nginx_Server中XFF字段依旧代表客户端的真实IP,并且伪造的IP并没有传递到Nginx_Server中。
使用代理的日志情况
此时我们的网络架构为:
# 客户端使用命令访问,我这里配置的是终端全局代理,所以不用单独指定代理参数
curl -XGET "http://47.x.x.156"
- Nginx_Server日志
43.x.x.74 - - [09/Dec/2019:14:58:02 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "43.x.x.74, 172.16.178.76"
- Proxy_1日志
43.x.x.74 - - [09/Dec/2019:14:58:02 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
- Proxy_2日志
172.16.178.76 - - [09/Dec/2019:14:58:02 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "43.x.x.74"
此时在Nginx_Server中XFF字段就是用户的代理IP,并且可以看到单独使用Nginx无法获取客户端的真实IP。
使用X-Forwarded-For与安全设置获取
在第一层代理服务器位置,处理用户传递的XFF信息,忽略用户的XFF值。
代理与服务器配置
- Nginx_Server配置:
vim /opt/nginx/conf/nginx.conf
,Nginx_Server配置不作任何修改,默认即可。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;}}
- Proxy_1配置:
vim /opt/nginx/conf/nginx.conf
,定义XFF为remote_addr。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.99;proxy_set_header X-Forwarded-For $remote_addr;}}
- Proxy_2配置:
vim /opt/nginx/conf/nginx.conf
,只做代理转发。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.100;}}
正常访问的日志情况
此时我们的网络架构为:
# 客户端使用命令访问
curl -XGET "http://10.10.10.98"
- Nginx_Server日志:
10.10.10.99 - - [09/Dec/2019:09:37:39 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.1"
- Proxy_1日志:
10.10.10.1 - - [09/Dec/2019:09:37:39 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
- Proxy_2日志:
10.10.10.98 - - [09/Dec/2019:09:37:39 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.1"
此时在Nginx_Server中XFF字段就代表用户的真实IP。
伪造XFF的日志情况
此时我们的网络架构为:
# 客户端访问时使用XFF
curl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
- Nginx_Server日志:
10.10.10.99 - - [09/Dec/2019:09:41:53 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.1"
- Proxy_1日志:
10.10.10.1 - - [09/Dec/2019:09:41:53 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "10.10.10.5"
- Proxy_2日志:
10.10.10.98 - - [09/Dec/2019:09:41:53 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.1"
此时在Nginx_Server中XFF字段依旧代表客户端的真实IP,并且伪造的IP并没有传递到Nginx_Server中。
使用代理的日志情况
此时我们的网络架构为:
# 客户端使用命令访问,我这里配置的是终端全局代理,所以不用单独指定代理参数
curl -XGET "http://47.x.x.156"
- Nginx_Server日志
172.16.178.77 - - [09/Dec/2019:15:07:45 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "43.x.x.74"
- Proxy_1日志
43.x.x.74 - - [09/Dec/2019:15:07:44 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
- Proxy_2日志
172.16.178.76 - - [09/Dec/2019:15:07:44 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "43.x.x.74"
此时在Nginx_Server中XFF字段就是用户的代理IP,并且可以看到单独使用Nginx无法获取客户端的真实IP。
使用X-Real-IP
代理与服务器配置
- Nginx_Server配置:
vim /opt/nginx/conf/nginx.conf
,将日志中的remote_addr替换为http_x_real_ip。
# 注意日志配置的第一个字段,将remote_addr修改为http_x_real_iplog_format main '$http_x_real_ip - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {root html;index index.html index.htm;}}
- Proxy_1配置:
vim /opt/nginx/conf/nginx.conf
,设置代理与x-real-ip字段。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.99;proxy_set_header X-Real-IP $remote_addr;}}
- Proxy_2配置:
vim /opt/nginx/conf/nginx.conf
,只做代理转发。
log_format main '$remote_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';access_log logs/access.log main;# ************* 省略了中间的配置server {listen 80;server_name localhost;#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.htm;# 注意这里的key value之间使用Tabproxy_pass http://10.10.10.100;}}
正常访问的日志情况
此时我们的网络架构为:
# 客户端使用命令访问
curl -XGET "http://10.10.10.98"
- Nginx_Server日志:
10.10.10.1 - - [09/Dec/2019:09:55:16 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
- Proxy_1日志:
10.10.10.1 - - [09/Dec/2019:09:55:16 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
- Proxy_2日志:
10.10.10.98 - - [09/Dec/2019:09:55:16 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
此时在Nginx_Server中第一个字段就代表客户端的真实IP。
伪造XFF的日志情况
此时我们的网络架构为:
# 客户端访问时使用XFF
curl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
- Nginx_Server日志:
10.10.10.1 - - [09/Dec/2019:10:00:38 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.5"
- Proxy_1日志
10.10.10.1 - - [09/Dec/2019:10:00:38 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "10.10.10.5"
- Proxy_2日志:
10.10.10.98 - - [09/Dec/2019:10:00:38 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "10.10.10.5"
此时在Nginx_Server中第一个字段依旧代表客户端真实IP,伪造的IP在XFF字段中。
使用代理的日志情况
此时我们的网络架构为:
# 客户端使用命令访问,我这里配置的是终端全局代理,所以不用单独指定代理参数
curl -XGET "http://47.x.x.156"
- Nginx_Server日志:
43.x.x.74 - - [09/Dec/2019:15:16:05 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
- Proxy_1日志:
43.x.x.74 - - [09/Dec/2019:15:16:05 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
- Proxy_2日志:
172.16.178.76 - - [09/Dec/2019:15:16:05 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
此时在Nginx_Server中第一个字段依旧代表客户端真实IP。
云厂商如何获取客户端真实IP
- 阿里云 如何获取客户端真实IP
- 使用知道创宇云安全后如何获取访客真实IP
总结一下
关于服务端获取客户端的真实IP可以实际场景实际分析吧!本文中提到的也只是一种很初级的网络架构。本文的适用范围相对也比较狭窄。
如果是复杂的网络结构,可以在每一层的产品上对对应厂商进行沟通:是否可以透传用户的真实IP,然后通过每一层的配置将真实IP传递到服务端使用合理的字段进行存储。
当然了安全本质就是不可信,传递的IP数据是否真实与客户端伪造技术、各层级之间相关配置都息息相关。IP维度也只是后端分析识别的一个维度而已,我们在尽可能保证这个维度的准确度时,不用太过钻牛角尖(除非是精准度要求非常高的场景)。对于中小型的企业,能结合IP、Location、Username、UA、Browser Banner、OS Banner等维度来做一些简单的关联分析即可。
以上就是小B做日志分析的前期调研第一篇,小B后续还会写一写关于日志分析的其他文章。(WeChat:Lzero2012)
参考资料
- Nginx多级反向代理下的IP透传
- Nginx之X-Forwarded-For中首个IP一定真实吗?
Nginx透过代理获取真实客户端IP相关推荐
- nginx curl命令有效 curl_setopt无效_日志分析系列(外传一):Nginx透过代理获取真实客户端IP...
本系列中的故事纯属虚构,如有雷同实属巧合 小B是Q公司的安全攻城狮,为了完成任务小B开始做起了调研(欲知背景如何,且听下回分说). 首先小B弄明白了Q公司的应用系统架构是:Client --> ...
- Nginx在多层代理下获取真实客户端IP地址
最近在研究nginx中如何获取真实客户端IP的方法.众所周知,在编译Nginx时,可通过添加http_realip_module模块来获取真实客户端IP地址.何为真实IP地址呢?请看下图,既获取到的真 ...
- 学习笔记 - Nginx在多层代理下获取真实客户端IP地址
最近在研究nginx中如何获取真实客户端IP的方法.众所周知,在编译Nginx时,可通过添加http_realip_module模块来获取真实客户端IP地址.何为真实IP地址呢?请看下图,既获取到的真 ...
- ABP vNext 审计日志获取真实客户端IP
背景 在使用ABP vNext时,当需要记录审计日志时,我们按照https://docs.abp.io/zh-Hans/abp/latest/Audit-Logging配置即可开箱即用,然而在实际生产 ...
- 服务器获取真实客户端 IP
0x01 先查个问题 测试环境微信支付通道提示网络环境未能通过安全验证,请稍后再试,出现这种情况一般首要 想到可能是双方网络交互中微信方验参与我们出现不一致,翻了下手册确定是这类问题开始排查环节 可能 ...
- php获取真实客户端IP方法
可能有时候简单地通过php自带的$_SERVER获取的IP参数并不能满足我们的需求,则需要我们自定义一个获取IP的方法,通过学习ECSHOP的源码,我们可以把获取IP的方法封装如下: <?php ...
- java获取f5服务器真实ip,F5服务器做负载均衡时WebService获取真实客户端IP地址
首先,我先来介绍一下什么是F5服务器,内容来自百度: 负载均衡,英文名称为Load Balance,其意思就是将负载(工作任务)进行平衡.分摊到多个操作单元上进行执行,例如Web服务器.FTP服务器. ...
- 网站配置了Cloudflare代理后,如何配置Nginx获取的真实客户端IP地址?
网站配置了Cloudflare代理后,如何配置Nginx获取的真实客户端IP地址? 这是一个很简单的问题,如何在后台获取真实的访问者IP地址? 网站为了避免有些不怀好意的访问者,不得不自动分析一下客户 ...
- NGINX前端代理TOMCAT取真实客户端IP
nginx前端代理tomcat取真实客户端IP 使用Nginx作为反向代理时,Tomcat的日志记录的客户端IP就不在是真实的客户端IP,而是Nginx代理的IP.要解决这个问题可以在Nginx配置一 ...
最新文章
- 易开发创始人潘俊勇:这些年我遇到的那些坑
- 解决gitosis.init.InsecureSSHKeyUsername: Username contains not allowed characters问题
- php群发不用foreach,如何在没有foreach的情况下使用PHP生成器?
- Springboot 项目启动后执行某些自定义代码
- 受迫阻尼 matlab 仿真,MATLAB系统仿真报告——有阻尼受迫振动系统
- Xshell连接Linux下Oracle无法回退的解决办法
- Apache Storm技术实战之2 -- BasicDRPCTopology
- ERROR: cuvid requested, but not all dependencies are satisfied: ffnvcodec
- 吴恩达深度学习笔记(Dropout正则化)
- oracle9i如何卸载,如何卸载oracle 9i
- 【研发管理】质量管理之约瑟夫·M.朱兰
- Android开发常用的模拟器
- 了解 云原生 和 边缘计算
- 傅里叶变换的性质(一)
- ISO8601标准时间格式
- 桌面上计算机图标被误删,电脑桌面图标被误删?两个方法轻松帮你找回,根本不需要重装系统...
- suse系统搭建ftp服务器,linux suse 搭建ftp服务器
- logstash日志收集走过的坑
- SpringCloud Alibaba Senta处理分布式事务
- 「Ceph源码分析」纠删码解码