詳解REMOTE_ADDR,HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR

weixin_34067049發表於2015-12-22

轉載自:http://www.cnblogs.com/lmule/archive/2010/10/15/1852020.html

 

看ecshop的lib_base.php的時候裡面獲取客戶端真實ip的函式(real_ip),有許多情況的判斷,主要判斷客戶端是否使用代理的情況,注意判斷順序,先判斷客戶端是否使用代理HTTP_X_FORWARDED_FOR

還是把原始碼附上吧

[php] view plaincopy
 
  1. <?php  
  2. /** 
  3.  * 獲得使用者的真實IP地址 
  4.  * 
  5.  * @access  public 
  6.  * @return  string 
  7.  */  
  8. function real_ip()  
  9. {  
  10.     static $realip = NULL;  
  11.    
  12.     if ($realip !== NULL)  
  13.     {  
  14.         return $realip;  
  15.     }  
  16.    
  17.     if (isset($_SERVER))  
  18.     {  
  19.         if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))  
  20.         {  
  21.             $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);  
  22.    
  23.             /* 取X-Forwarded-For中第一個非unknown的有效IP字串 */  
  24.             foreach ($arr AS $ip)  
  25.             {  
  26.                 $ip = trim($ip);  
  27.    
  28.                 if ($ip != 'unknown')  
  29.                 {  
  30.                     $realip = $ip;  
  31.    
  32.                     break;  
  33.                 }  
  34.             }  
  35.         }  
  36.         elseif (isset($_SERVER['HTTP_CLIENT_IP']))  
  37.         {  
  38.             $realip = $_SERVER['HTTP_CLIENT_IP'];  
  39.         }  
  40.         else  
  41.         {  
  42.             if (isset($_SERVER['REMOTE_ADDR']))  
  43.             {  
  44.                 $realip = $_SERVER['REMOTE_ADDR'];  
  45.             }  
  46.             else  
  47.             {  
  48.                 $realip = '0.0.0.0';  
  49.             }  
  50.         }  
  51.     }  
  52.     else  
  53.     {  
  54.         if (getenv('HTTP_X_FORWARDED_FOR'))  
  55.         {  
  56.             $realip = getenv('HTTP_X_FORWARDED_FOR');  
  57.         }  
  58.         elseif (getenv('HTTP_CLIENT_IP'))  
  59.         {  
  60.             $realip = getenv('HTTP_CLIENT_IP');  
  61.         }  
  62.         else  
  63.         {  
  64.             $realip = getenv('REMOTE_ADDR');  
  65.         }  
  66.     }  
  67.    
  68.     preg_match("/[\d\.]{7,15}/", $realip, $onlineip);  
  69.     $realip = !empty($onlineip[0]) ? $onlineip[0] : '0.0.0.0';  
  70.    
  71.     return $realip;  
  72. }  
  73. ?>  

 

順便說下$_SERVER和getenv的區別,getenv不支援IIS的isapi方式執行的php

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

 

      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。

  完全用代理伺服器的資訊替代了您的所有資訊,就象您就是完全使用那臺代理伺服器直接訪問物件。

REMOTE_ADDR 是你的客戶端跟你的伺服器“握手”時候的IP。如果使用了“匿名代理”,REMOTE_ADDR將顯示代理伺服器的IP。
HTTP_CLIENT_IP 是代理伺服器傳送的HTTP頭。如果是“超級匿名代理”,則返回none值。同樣,REMOTE_ADDR也會被替換為這個代理伺服器的IP。
$_SERVER['REMOTE_ADDR']; //訪問端(有可能是使用者,有可能是代理的)IP
$_SERVER['HTTP_CLIENT_IP'];  //代理端的(有可能存在,可偽造)
$_SERVER['HTTP_X_FORWARDED_FOR']; //使用者是在哪個IP使用的代理(有可能存在,也可以偽造)

 

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

在WEB開發中.我們可能都習慣使用下面的程式碼來獲取客戶端的IP地址: 
C#程式碼

複製程式碼 程式碼如下:

//優先取得代理IP 
string IP = Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; 
if (string.IsNullOrEmpty(IP)) { 
//沒有代理IP則直接取連線客戶端IP 
IP = Request.ServerVariables["REMOTE_ADDR"]; 
}


上面程式碼看來起是正常的.可惜這裡卻隱藏了一個隱患!!因為"HTTP_X_FORWARDED_FOR"這個值是通過獲取HTTP頭的"X_FORWARDED_FOR"屬性取得.所以這裡就提供給惡意破壞者一個辦法:可以偽造IP地址!! 
下面是測試程式碼:

複製程式碼 程式碼如下:

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://localhost/ip.aspx"); 
request.Headers.Add("X_FORWARDED_FOR", "0.0.0.0"); 
HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 
StreamReader stream = new StreamReader(response.GetResponseStream()); 
string IP = stream.ReadToEnd(); 
stream.Close(); 
response.Close(); 
request = null;


"ip.aspx"檔案程式碼:

複製程式碼 程式碼如下:

Response.Clear(); 
//優先取得代理IP 
string IP = Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; 
if (string.IsNullOrEmpty(IP)) 

//沒有代理IP則直接取客戶端IP 
IP = Request.ServerVariables["REMOTE_ADDR"]; 

Response.Write(IP); 
Response.End();


這樣.當測試程式碼中去訪問ip.aspx檔案時."string IP = stream.ReadToEnd();"這段程式碼取到的IP資料就是"0.0.0.0"!!!!(呵.在真實情況下.這樣的IP地址肯定不是我們想要的結果.而在有些投票系統中限制一個IP只能投1次票時,如果也是用類似的程式碼取得對方IP然後再判斷的話.呵呵.限制就失效咯)...

 

或者如果你用上面程式碼獲取IP地址後後面又不再進行資料判斷的話也許還能更進一步進行資料破壞!! 
比如你用類似上面的程式碼中獲取IP地址就直接有這樣的SQL語句: 
string sql = "INSERT INTO (IP) VALUE ('" + IP + "')"; 
那麼也許破壞者還可以進行SQL隱碼攻擊進行資料破壞!!

這樣看來利用"HTTP_X_FORWARDED_FOR"這個屬性獲取客戶端IP的方法就不再可取了.-_-# 但如果不用這種方法.那麼那些真正使用了代理伺服器的人.我們又不能再獲取到他們的真實IP地址(因為某些代理伺服器會在"X_FORWARDED_FOR"這個HTTP頭裡加上訪問使用者真正的IP地址).呵.現實就是這樣,某種東西都有有得必有失...

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