一、如果沒有使用代理服務器,
REMOTE_ADDR= 客戶端IP HTTP_X_FORWARDED_FOR= 沒數值或不顯示
$ip = $_SERVER['REMOTE_ADDR'];
二、使用透明代理,
REMOTE_ADDR= 最后一個代理服務器 IP
HTTP_X_FORWARDED_FOR= 客戶端真實 IP (經過多個代理服務器時,這個值類似:221.5.252.160, 203.98.182.163, 203.129.72.215)
這類代理還會將客戶真實ip發送到請求對象,無法隱藏真實ip。
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
三、使用普通匿名代理服務器,
REMOTE_ADDR= 最后一個代理服務器 IP
HTTP_X_FORWARDED_FOR = 代理服務器 IP (經過多個代理服務器時,這個值類似:203.98.182.163, 203.98.182.163, 203.129.72.215)
這樣就隱藏了客戶端的真實ip,但服務器會知道客戶端是通過代理服務器去訪問的。
四、使用欺騙性代理服務器,
REMOTE_ADDR= 代理服務器 IP
HTTP_X_FORWARDED_FOR= 隨機的 IP(經過多個代理服務器時,這個值類似:220.4.251.159, 203.98.182.163, 203.129.72.215)
服務器可以識別到時通過代理服務器訪問的,但發送給目標服務器的是虛假ip。
五、使用高匿名代理,
REMOTE_ADDR= 代理服務器 IP HTTP_X_FORWARDED_FOR= 沒數值或不顯示
使用這種代理時,不同瀏覽器不同設備會返回不同的ip頭信息,因此PHP使用$_SERVER["REMOTE_ADDR"] 、$_SERVER["HTTP_X_FORWARDED_FOR"] 獲取的值可能是空值也可能是“unknown”值。
PHP獲取ip代碼方法1:
function getip() { static $realip; if (isset($_SERVER)) { if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $realip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else if (isset($_SERVER['HTTP_CLIENT_IP'])) { $realip = $_SERVER['HTTP_CLIENT_IP']; } else { $realip = $_SERVER['REMOTE_ADDR']; } } else { if (getenv('HTTP_X_FORWARDED_FOR')) { $realip = getenv('HTTP_X_FORWARDED_FOR'); } else if (getenv('HTTP_CLIENT_IP')) { $realip = getenv('HTTP_CLIENT_IP'); } else { $realip = getenv('REMOTE_ADDR'); } } return $realip; }
PHP獲取ip代碼方法2:
function getip(){ $ip=false; if(!empty($_SERVER['HTTP_CLIENT_IP'])){ $ip=$_SERVER['HTTP_CLIENT_IP']; } if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])){ $ips=explode (', ', $_SERVER['HTTP_X_FORWARDED_FOR']); if($ip){ array_unshift($ips, $ip); $ip=FALSE; } for ($i=0; $i < count($ips); $i++){ if(!eregi ('^(10│172.16│192.168).', $ips[$i])){ $ip=$ips[$i]; break; } } } return ($ip ? $ip : $_SERVER['REMOTE_ADDR']); }
根據ip獲取歸屬地信息
/** 獲取 IP 地理位置 * 淘寶IP接口 * @Return: array */ function getCity($ip = '') { $url="http://ip.taobao.com/service/getIpInfo.php?ip=".$ip; $ip=json_decode(file_get_contents($url)); if((string)$ip->code=='1'){ return false; } $data = (array)$ip->data; return $data; }
現狀
目前主流的函數方法:
<?php
function getIp() { if ($_SERVER["HTTP_CLIENT_IP"] && strcasecmp($_SERVER["HTTP_CLIENT_IP"], "unknown")) { $ip = $_SERVER["HTTP_CLIENT_IP"]; } else { if ($_SERVER["HTTP_X_FORWARDED_FOR"] && strcasecmp($_SERVER["HTTP_X_FORWARDED_FOR"], "unknown")) { $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; } else { if ($_SERVER["REMOTE_ADDR"] && strcasecmp($_SERVER["REMOTE_ADDR"], "unknown")) { $ip = $_SERVER["REMOTE_ADDR"]; } else { if (isset ($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown") ) { $ip = $_SERVER['REMOTE_ADDR']; } else { $ip = "unknown"; } } } } return ($ip); } echo getIp();
測試
curl偽造IP請求:
$ch = curl_init('http://localhost/ip.php'); //通用設置 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);//不直接輸出 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);//跟蹤重定向 //偽造請求頭 $ip = mt_rand(1, 255) . '.' . mt_rand(1, 255) . '.' . mt_rand(1, 255) . '.' . mt_rand(1, 255); $header = [ 'CLIENT-IP: ' . $ip, 'X-FORWARDED-FOR: ' . $ip, 'X-REAL-IP: ' . $ip, 'Accept-Language: zh-CN,zh;', ]; curl_setopt($ch, CURLOPT_HTTPHEADER, $header); $html = curl_exec($ch); curl_close($ch); echo $html;
輸出SERVER數組,發現【HTTP_CLIENT_IP】、【HTTP_X_FORWARDED_FOR】、【HTTP_X_REAL_IP】是隨機變動的IP地址。
主流方法根本不安全!
分析
為什么?
HTTP_CLIENT_IP:存在於http請求的header
HTTP_X_FORWARDED_FOR:請求轉發路徑,客戶端IP,代理1IP,代理2IP......
HTTP_X_REAL_IP:這個用得比較少,暫不討論。
這三個值都是從HTTP請求頭獲取的,所以並不可靠!
REMOTE_ADDR
REMOTE_ADDR:是直接從TCP中獲取的IP,基本不會被偽造!
返回查看$_SERVER數組,發現【REMOTE_ADDR】顯示正確的IP!
所以直接用 $_SERVER['REMOTE_ADDR'] 就解決問題了?
其實還不行,如果客戶端和服務器之間存在代理服務器,【REMOTE_ADDR】的值是最后一個代理服務器的IP!
只有第一台接收客戶端請求的代理服務器的【REMOTE_ADDR】值才是客戶的真實IP地址,要把該值傳遞下去!
解決方案
1. 客戶端和服務器直連
<?php
function get_client_ip() { $ip = $_SERVER['REMOTE_ADDR']; return $ip; }
2. 客戶端和服務器存在中間代理
第一層nginx代理設置:
proxy_set_header X-Forwarded-For $remote_addr;
其他層nginx代理設置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
PHP代碼:
<?php
function get_client_ip() { $ip = null; if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $ip = trim(current($ip)); } return $ip; }
另:
<?php
function getIP()
{
static $realip;
if (isset($_SERVER)){
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])){
$realip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else if (isset($_SERVER["HTTP_CLIENT_IP"])) {
$realip = $_SERVER["HTTP_CLIENT_IP"];
} else {
$realip = $_SERVER["REMOTE_ADDR"];
}
} else {
if (getenv("HTTP_X_FORWARDED_FOR")){
$realip = getenv("HTTP_X_FORWARDED_FOR");
} else if (getenv("HTTP_CLIENT_IP")) {
$realip = getenv("HTTP_CLIENT_IP");
} else {
$realip = getenv("REMOTE_ADDR");
}
}
return $realip;
}
echo getIP();
?>
簡易
<?php
$ip = $_SERVER['REMOTE_ADDR'];
echo $ip;
?>
