本系列中的故事純屬虛構,如有雷同實屬巧合
小B是Q公司的安全攻城獅,為了完成任務小B開始做起了調研(欲知背景如何,且聽下回分說)。
首先小B弄明白了Q公司的應用系統架構是: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之間使用Tab            proxy_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之間使用Tab            proxy_pass  http://10.10.10.100;        }    }
正常訪問的日志情況
此時我們的網絡架構為:
# 客戶端使用命令訪問curl -XGET "http://10.10.10.98"
10.10.10.99 - - [11/Dec/2019:09:04:42 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
10.10.10.1 - - [11/Dec/2019:09:04:43 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
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的日志情況
此時我們的網絡架構為:
# 客戶端訪問時使用XFFcurl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
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"
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"
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之間使用Tab            proxy_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之間使用Tab            proxy_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"
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"
10.10.10.1 - - [09/Dec/2019:09:19:21 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
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的日志情況
此時我們的網絡架構為:
# 客戶端訪問時使用XFFcurl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
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"
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"
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"
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"
43.x.x.74 - - [09/Dec/2019:14:58:02 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
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之間使用Tab            proxy_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之間使用Tab            proxy_pass  http://10.10.10.100;        }    }
正常訪問的日志情況
此時我們的網絡架構為:
# 客戶端使用命令訪問curl -XGET "http://10.10.10.98"
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"
10.10.10.1 - - [09/Dec/2019:09:37:39 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
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的日志情況
此時我們的網絡架構為:
# 客戶端訪問時使用XFFcurl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
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"
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"
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"
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"
43.x.x.74 - - [09/Dec/2019:15:07:44 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
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_ip  log_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之間使用Tab            proxy_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之間使用Tab            proxy_pass  http://10.10.10.100;        }    }
正常訪問的日志情況
此時我們的網絡架構為:
# 客戶端使用命令訪問curl -XGET "http://10.10.10.98"
10.10.10.1 - - [09/Dec/2019:09:55:16 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
10.10.10.1 - - [09/Dec/2019:09:55:16 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
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的日志情況
此時我們的網絡架構為:
# 客戶端訪問時使用XFFcurl -XGET "http://10.10.10.98" -H "X-Forwarded-For: 10.10.10.5"
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"
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"
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"
43.x.x.74 - - [09/Dec/2019:15:16:05 +0800] "GET / HTTP/1.0" 200 612 "-" "curl/7.64.1" "-"
43.x.x.74 - - [09/Dec/2019:15:16:05 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.64.1" "-"
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(https://help.aliyun.com/document_detail/54007.html)
- 使用知道創宇云安全后如何獲取訪客真實IP(http://help.yunaq.com/faq/67/index.html)
總結一下
關于服務端獲取客戶端的真實IP可以實際場景實際分析吧!本文中提到的也只是一種很初級的網絡架構。本文的適用范圍相對也比較狹窄。
如果是復雜的網絡結構,可以在每一層的產品上對對應廠商進行溝通:是否可以透傳用戶的真實IP,然后通過每一層的配置將真實IP傳遞到服務端使用合理的字段進行存儲。
當然了安全本質就是不可信,傳遞的IP數據是否真實與客戶端偽造技術、各層級之間相關配置都息息相關。IP維度也只是后端分析識別的一個維度而已,我們在盡可能保證這個維度的準確度時,不用太過鉆牛角尖(除非是精準度要求非常高的場景)。對于中小型的企業,能結合IP、Location、Username、UA、Browser Banner、OS Banner等維度來做一些簡單的關聯分析即可。
以上就是小B做日志分析的前期調研第一篇,小B后續還會寫一寫關于日志分析的其他文章。(WeChat:Lzero2012)
參考資料
- Nginx多級反向代理下的IP透傳(https://www.cnblogs.com/tea-melon/p/10977516.html)
- Nginx之X-Forwarded-For中首個IP一定真實嗎?(https://juejin.im/entry/5bbb6e90f265da0a89304a43)
總結
                            
                                以上是生活随笔為你收集整理的nginx curl命令有效 curl_setopt无效_日志分析系列(外传一):Nginx透过代理获取真实客户端IP...的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。