20120917 @鄭昀匯總
外界流傳的JAVA/PHP伺服器端擷取用戶端IP都是這麼取的:虛擬碼:1)ip = request.getHeader("
X-FORWARDED-FOR")
可偽造,參考附錄A2)如果該值為空白或數組長度為0或等於"unknown",那麼:ip = request.getHeader("Proxy-Client-IP")3)如果該值為空白或數組長度為0或等於"unknown",那麼:ip = request.getHeader("WL-Proxy-Client-IP")4)如果該值為空白或數組長度為0或等於"unknown",那麼:ip = request.getHeader("HTTP_CLIENT_IP")
可偽造5)如果該值為空白或數組長度為0或等於"unknown",那麼:ip = request.
getRemoteAddr()
可對於匿名Proxy 伺服器,可隱匿原始ip,參考附錄B 之所以搞這麼麻煩,是因為存在很多種網路結構,如 Nginx+Resin、Apache+WebLogic、Squid+Nginx。下面挨個兒講一下。鄭昀 :△ 首先,明確一下,Nginx 配置一般如下所示: location / {
proxy_pass http://yourdomain.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}注意看紅色字型,這些配置與下面的闖關拿IP有關。 ———————————————————————————————
——第一關|X-Forwarded-For :背景——這是一個 Squid 開發的欄位,並非 RFC 標準。簡稱
XFF 頭,只有在通過了 HTTP 代理或者負載平衡伺服器時才會添加該項。在 Squid 開發文檔中可以找到該項的詳細介紹。XFF 格式如下:X-Forwarded-For: client1, proxy1, proxy2可以看出,XFF 頭資訊可以有多個,中間用逗號分隔,第一項為真實的用戶端ip,剩下的就是曾經經過的代理或負載平衡伺服器的ip地址。
——第一關|X-Forwarded-For :情境=用戶端--CDN--Nginx——當使用者請求經過 CDN 後到達 Nginx 負載平衡伺服器時,其 XFF 頭資訊應該為 “用戶端IP,CDN的IP”。一般情況下CDN服務商出於自身安全考慮會將屏蔽CDN的ip,只保留用戶端ip。那麼要求標頭到達 Nginx 時:
- 在預設情況下,Nginx 並不會對 XFF 頭做任何處理
- 此時 Nginx 後面的 Resin/Apache/Tomcat 通過 request.getHeader("X-FORWARDED-FOR") 獲得的ip仍然是原始ip。
- 當 Nginx 設定 X-Forwarded-For 等於 $proxy_add_x_forwarded_for 時:
- 如果從CDN過來的請求沒有設定 XFF 頭(通常這種事情不會發生),XFF 頭為 CDN 的ip
- 如果 CDN 設定了 XFF 頭,我們這裡又設定了一次,且值為$proxy_add_x_forwarded_for 的話:
- XFF 頭為“用戶端IP,Nginx負載平衡伺服器IP”,這樣取第一個值即可
- 這也就是大家所常見的情境!
綜上所述,XFF 頭在的情境,Resin 通過 request.getHeader("X-FORWARDED-FOR") 獲得的ip字串,做一個split,第一個元素就是原始ip。那麼,XFF 頭可以偽造嗎?
——第一關|X-Forwarded-For :偽造——
可以偽造。XFF 頭僅僅是 HTTP Headers 中的一分子,自然是可以隨意增刪改的。如附錄A所示。很多投票系統都有此漏洞,它們簡單地取 XFF 頭中定義的ip地址設定為來源地址,因此第三方可以偽造任何ip投票。 ———————————————————————————————
——第二和第三關|Proxy-Client-IP/WL-
Proxy-Client-IP
:背景——Proxy-Client-IP 欄位和 WL-Proxy-Client-IP 欄位只在 Apache(Weblogic Plug-In Enable)+WebLogic 搭配下出現,其中“WL” 就是 WebLogic 的縮寫。即訪問路徑是:
Client -> Apache WebServer + Weblogic http plugin -> Weblogic Instances
所以這兩關對於我們來說僅僅是相容而已,怕你突然把 Nginx+Resin 換成 Apache+WebLogic 。也可以直接忽略這兩個欄位。 ———————————————————————————————
——第四關|HTTP-Client-IP
:背景——HTTP_CLIENT_IP 是Proxy 伺服器發送的HTTP頭。很多時候 Nginx 配置中也並沒有下面這項:
proxy_set_header HTTP_CLIENT_IP $remote_addr;
所以本關也可以忽略。鄭昀 :△ ———————————————————————————————
——第五關| request.getRemoteAddr() :背景——從 request.getRemoteAddr() 函數的定義看: Returns the Internet Protocol (IP) address of the client or last proxy that sent the request.
實際上,REMOTE_ADDR 是用戶端跟伺服器“握手”時的IP,但如果使用了“匿名代理”,REMOTE_ADDR 將顯示Proxy 伺服器的ip,或者最後一個Proxy 伺服器的ip。請參考附錄B。
綜上,
java/php 裡拿到的ip地址可能是偽造的或Proxy 伺服器的ip。
鄭昀 :△
+++附錄A XFF 與 Nginx 配置的測試案例+++測試環境: nginx+resin
內網IP:172.16.100.10
用戶端IP:123.123.123.123
測試頁面: test.jsp
<%
out.println("x-forwarded-for: " + request.getHeader("x-forwarded-for"));
out.println("remote hosts: " + request.getRemoteAddr());
%>
nginx 配置一proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; wget測試wget -O aa --header="X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"頁面返回結果:x-forwarded-for: 192.168.0.1, 123.123.123.123remote hosts: 172.16.100.10 curl測試curl -H "X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"x-forwarded-for: 192.168.0.1, 123.123.123.123remote hosts: 172.16.100.10
nginx 配置二
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
wget測試:
wget -O aa --header="X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"
頁面返回結果:
x-forwarded-for: 123.123.123.123
remote hosts: 172.16.100.10
curl測試
curl -H "X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"
x-forwarded-for: 123.123.123.123
remote hosts: 172.16.100.10
測試結果:
1、配置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
增加了一個真實ip X-Forwarded-For,並且順序是增加到了“後面”。
2、配置
proxy_set_header X-Forwarded-For $remote_addr;
清空了用戶端偽造傳入的X-Forwarded-For,
保證了使用 request.getHeader("x-forwarded-for") 擷取的ip為真實ip,
或者用“,”分隔,截取 X-Forwarded-For 最後的值。
+++附錄B 搜狗瀏覽器高速模式的測試案例+++訪問路徑:搜狗瀏覽器“高速”模式(即使用代理)-->LVS-->Apache獲得的值為:x-forwarded-for:180.70.92.43 (即真實ip)Proxy-Client-IP:nullWL-Proxy-Client-IP:null getRemoteAddr:123.126.50.185 (即搜狗代理ip)
×××參考資源:×××1,http://bbs.linuxtone.org/thread-9050-1-1.html2,http://hi.baidu.com/thinkinginlamp/item/e2cf05263eb4d18e6e2cc3e63,http://bbs.chinaunix.net/thread-3659453-1-1.html 贈圖2枚: