HOWTO · PHP
在 PHP 中獲取使用者 IP 地址
本教程演示如何在 PHP 中獲取使用者的 IP 地址
本頁內容
若要讀取與 PHP 請求相關的網路位址,請先使用 $_SERVER['REMOTE_ADDR']。它會指出連線至網頁伺服器的對端。若應用程式前方設有反向代理,這個對端可能是代理伺服器,而非訪客。只有在應用程式明確設定信任提供並驗證代理標頭的代理伺服器時,才能使用相關請求標頭。
使用 $_SERVER['REMOTE_ADDR'] 讀取連線位址
$_SERVER 是 PHP 超級全域變數,包含伺服器與請求資訊。其中的 REMOTE_ADDR 元素會保存連線至處理該請求之伺服器的對端位址。直接連線時,這通常是發起連線的用戶端;若位於負載平衡器或反向代理之後,則通常會是代理伺服器的位址。PHP 收到的值取決於網頁伺服器與代理伺服器的設定,因此不應將它描述成一定是終端使用者的位址。
本機請求的對端可能是 IPv6 回送位址 ::1。回送位址指向本機;::1 是 IPv6 回送位址,相當於 IPv4 的 127.0.0.1。實際值會依本機伺服器與連線而異。請參閱這篇回送位址說明。
以下可執行範例會設定一個具代表性的伺服器值,讓查詢過程完整自足。範例將 display_output 設為 false,因此會執行查詢,但不會輸出顯示內容;在應用程式中,請依自己的回應或記錄需求使用該值。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
<?php
$_SERVER['REMOTE_ADDR'] = '::1';
$peerAddress = $_SERVER['REMOTE_ADDR'];
$display_output = false;
if ($display_output) {
echo "The connected peer is: " . $peerAddress . PHP_EOL;
}
?>
實際處理請求時,請使用伺服器提供的 $_SERVER['REMOTE_ADDR'] 值,而不要自行指定範例值。PHP 手冊的伺服器變數參考說明了可用的 $_SERVER 項目。
只有在信任代理伺服器時才解讀代理標頭
PHP 可能會將請求標頭公開為 $_SERVER 項目,例如 HTTP_X_FORWARDED_FOR 和 HTTP_CLIENT_IP。這些項目存在,不代表其中包含訪客的位址:用戶端可以自行傳送標頭值,而代理伺服器也可能保留、附加或替換這些值。尤其是 HTTP_X_FORWARDED_FOR,可能包含以逗號分隔的位址鏈,而非單一位址。若未依照基礎架構中明確制定的信任規則處理,請勿直接取用第一個值並稱它為真實用戶端位址。
只有在連線對端符合應用程式明確信任的代理伺服器,且該代理伺服器已設定為移除或驗證用戶端提供的轉送標頭時,才能使用轉送值。受信任代理清單必須符合你的部署環境;請勿將範例位址直接複製為正式環境設定。若對端不受信任,請使用 REMOTE_ADDR。HTTP_CLIENT_IP 也有相同的信任問題,不應被當成共享網際網路連線的特殊解答。
這段可執行程式碼範例模擬一種部署方式:受信任的代理伺服器會以單一已驗證位址覆寫 X-Forwarded-For。請將範例代理位址與驗證政策替換成你基礎架構所定義的值。範例會針對這些具代表性的值輸出所選的轉送位址;它不會解析多代理伺服器位址鏈。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
<?php
$_SERVER['REMOTE_ADDR'] = '192.0.2.10';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '198.51.100.24';
$trustedProxies = ['192.0.2.10'];
$peerAddress = $_SERVER['REMOTE_ADDR'];
if (in_array($peerAddress, $trustedProxies, true)
&& isset($_SERVER['HTTP_X_FORWARDED_FOR'])
&& filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP) !== false) {
$clientAddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$clientAddress = $peerAddress;
}
echo "Selected address: " . $clientAddress . PHP_EOL;
?>
Selected address: 198.51.100.24
filter_var() 的文件說明了此處使用的驗證函式。驗證字串是 IP 位址,並不能證明它來自受信任的來源;信任判斷應由對端檢查與代理伺服器設定共同決定。如果代理伺服器轉送的是位址鏈,請使用框架,或採用經過仔細審查的程式碼,並依照部署環境的確切受信任代理規則逐一處理鏈中的位址。
使用條件運算式簡潔地設定備援順序
容易閱讀的 if/elseif/else 區塊通常比較容易稽核,尤其是包含代理信任檢查時。條件運算式可以簡潔地表達相同選擇,但不會因此讓不受信任的標頭變得安全。以下可執行範例會檢查代理是否受信任,並採用前述相同的單一位址覆寫政策。範例中的伺服器值僅具代表性;程式會輸出所選的轉送位址。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
<?php
$_SERVER['REMOTE_ADDR'] = '192.0.2.10';
$_SERVER['HTTP_X_FORWARDED_FOR'] = '198.51.100.24';
$trustedProxies = ['192.0.2.10'];
$peerAddress = $_SERVER['REMOTE_ADDR'];
$forwardedAddress = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
$clientAddress = in_array($peerAddress, $trustedProxies, true)
&& filter_var($forwardedAddress, FILTER_VALIDATE_IP) !== false
? $forwardedAddress
: $peerAddress;
echo "Selected address: " . $clientAddress . PHP_EOL;
?>
Selected address: 198.51.100.24
備援順序有其用意:只有在連線對端位於受信任代理清單中時,才接受已驗證的轉送位址;否則保留連線對端位址。使用括號可以讓巢狀條件運算式更清楚,但巢狀三元運算式較難維護,也不能取代信任檢查。請讓代理伺服器的標頭處理方式與應用程式的允許清單保持一致,避免用戶端將自己的標頭冒充為受信任輸入。