擷取使用者的真實 ip,對于安全業務來說非常重要。
阿裡雲場景下一個http 請求一般為:
使用者ip --> ddos 高防 ip ->slb ip
對于web伺服器來說,主要是通過兩種方式擷取 ip
與伺服器建立tcp連接配接的位址 remote address
通過 http header 的 x-forwarded-for 字段
對應的 php 變量如下
與伺服器建立tcp連接配接的 ip
無法僞造,很合适作為使用者真實ip
但是 http 請求經過七層代理後,就不是使用者ip了,一般為slb ip
通過http header傳遞給服務端
可以僞造,有可能擷取的資料不準确,還可能引發 xss,sql 注入等問題
x-forwarded-for :格式如下
x-forwarded-for: client, proxy1, proxy2
如果一個 http 請求到達伺服器之前,經過了三個代理 proxy1、proxy2、proxy3,ip 分别為 ip1、ip2、ip3,使用者真實 ip 為 ip0,服務端最終會收到以下資訊:
x-forwarded-for: ip0, ip1, ip2
proxy3 直連伺服器,它會給 xff 追加 ip2,表示它是在幫 proxy2 轉發請求。清單中并沒有 ip3,ip3 可以在服務端通過 remote_addr 字段獲得。
如果使用者篡改 x-forwarded-for
x-forwarded-for: ip100, ip0, ip1, ip2
我們怎麼通過這兩個字段擷取使用者真實 ip 呢?
如果 http 請求沒有經過七層代理,直接讀取 remote_addr 字段最好
如果 http 請求經過七層代理, 我們隻能從 x-forwarded-for 擷取,但是我們必須過濾使用者僞造資料,擷取真實的使用者 ip
如上面例子 ip1, ip2是我們自己配置的 ip,可信任的ip,過濾掉可信任的代理ip,最後一個不可信任的ip就是使用者ip。
要解決的問題如下
nginx的通路日志,記錄使用者真實ip,現在記錄的是remote address。
php擷取使用者真實ip,如果我們擷取x-forwarded-for第一個ip。容易被使用者篡改。
調研過程中,發現ngx_http_realip_module 滿足我們需求, 當擷取了使用者真實ip,會指派給 nginx變量 $remote_addr 變量,那麼 php的 $_server['remote_addr'] 也變成了使用者真實ip。
與web伺服器建議tcp連接配接的ip 存儲在變量 $realip_remote_addr 中。
set_ip_from
設定信任 ip,就是我們自己的代理ip,可以設定多個
real_ip_header
設定請求頭字段,表示從x-forwarded-for字段擷取使用者 ip
real_ip_recursive
off:如果remote address 能夠比對set_real_ip_from 的ip,使用者 x-forwarded-for最後一個 ip 作為使用者ip。
on: 如果remote address 能夠比對set_real_ip_from 的ip,使用者 x-forwarded-for最後一個不可信任 ip 作為使用者ip。
我們的配置如下,注意還沒有上線
nginx 配置如下
php 擷取使用者ip
$_sever['remote_addr']
<a href="https://imququ.com/post/x-forwarded-for-header-in-http.html">https://imququ.com/post/x-forwarded-for-header-in-http.html</a>
<a href="http://nginx.org/en/docs/http/ngx_http_realip_module.html">http://nginx.org/en/docs/http/ngx_http_realip_module.html</a>
<a href="http://baike.baidu.com/item/iana%e4%bf%9d%e7%95%99%e5%9c%b0%e5%9d%80?fr=aladdin">http://baike.baidu.com/item/iana%e4%bf%9d%e7%95%99%e5%9c%b0%e5%9d%80?fr=aladdin</a>