客戶端的IP地址僞造、CDN、反向代理、獲取的那些事兒

獲取用戶IP地址的三個屬性的區別(HTTP_X_FORWARDED_FOR,HTTP_VIA,REMOTE_ADDR)

一、沒有使用代理服務器的情況:

      REMOTE_ADDR = 您的 IP
      HTTP_VIA = 沒數值或不顯示
      HTTP_X_FORWARDED_FOR = 沒數值或不顯示

二、使用透明代理服務器的情況:Transparent Proxies

      REMOTE_ADDR = 最後一個代理服務器 IP 
      HTTP_VIA = 代理服務器 IP
      HTTP_X_FORWARDED_FOR = 您的真實 IP ,經過多個代理服務器時,這個值類似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

   這類代理服務器還是將您的信息轉發給您的訪問對象,無法達到隱藏真實身份的目的。

三、使用普通匿名代理服務器的情況:Anonymous Proxies

      REMOTE_ADDR = 最後一個代理服務器 IP 
      HTTP_VIA = 代理服務器 IP
      HTTP_X_FORWARDED_FOR = 代理服務器 IP ,經過多個代理服務器時,這個值類似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

   隱藏了您的真實IP,但是向訪問對象透露了您是使用代理服務器訪問他們的。

四、使用欺騙性代理服務器的情況:Distorting Proxies

      REMOTE_ADDR = 代理服務器 IP 
      HTTP_VIA = 代理服務器 IP 
      HTTP_X_FORWARDED_FOR = 隨機的 IP ,經過多個代理服務器時,這個值類似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

   告訴了訪問對象您使用了代理服務器,但編造了一個虛假的隨機IP代替您的真實IP欺騙它。

五、使用高匿名代理服務器的情況:High Anonymity Proxies (Elite proxies)

      REMOTE_ADDR = 代理服務器 IP
      HTTP_VIA = 沒數值或不顯示
      HTTP_X_FORWARDED_FOR = 沒數值或不顯示 ,經過多個代理服務器時,這個值類似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

   完全用代理服務器的信息替代了您的所有信息,就象您就是完全使用那臺代理服務器直接訪問對象。



------------------------------------------------------------------------------------------------------------

外界流傳的JAVA/PHP服務器端獲取客戶端IP都是這麼取的:
僞代碼:
1)ip = request.getHeader("X-FORWARDED-FOR")
    可僞造,參考附錄A
2)如果該值爲空或數組長度爲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()
    可對於匿名代理服務器,可隱匿原始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
      • 此時相對於 Nginx 來說,客戶端就是 CDN 
    • 如果 CDN 設置了 XFF 頭,我們這裏又設置了一次,且值爲$proxy_add_x_forwarded_for 的話:
      • XFF 頭爲“客戶端IP,Nginx負載均衡服務器IP”,這樣取第一個值即可
      • 這也就是大家所常見的場景!
http://images.cnblogs.com/cnblogs_com/zhengyun_ustc/255879/o_client-cdn-nginx-resin.png
綜上所述,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 是代理服務器發送的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 將顯示代理服務器的ip,或者最後一個代理服務器的ip。請參考附錄B。 
 
綜上,
java/php 裏拿到的ip地址可能是僞造的或代理服務器的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.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: 192.168.0.1, 123.123.123.123
remote 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:null
WL-Proxy-Client-IP:null 
getRemoteAddr:123.126.50.185  (即搜狗代理ip)
 
 
×××參考資源:×××
1,http://bbs.linuxtone.org/thread-9050-1-1.html
2,http://hi.baidu.com/thinkinginlamp/item/e2cf05263eb4d18e6e2cc3e6
3,http://bbs.chinaunix.net/thread-3659453-1-1.html

發佈了46 篇原創文章 · 獲贊 33 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章