首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 取得用戶真實 IP 完整指南:HTTP_X_FORWARDED_FOR、HTTP_VIA、REMOTE_ADDR 差異總結與最佳實踐

     編輯:PHP 相關知識

前言:為什麼取得真實 IP 這麼麻煩?

在現代網路架構中,用戶請求常經過多層代理伺服器(負載平衡器、CDN、反向代理、正向代理等)才抵達應用伺服器。PHP 的 $_SERVER['REMOTE_ADDR'] 僅能取得**直接連線來源的 IP**,若中間有代理,這將是代理伺服器的 IP 而非用戶真實 IP。正確解析真實 IP 需理解各種代理類型與 HTTP 標頭行為差異。

五種代理類型與 HTTP 標頭對照表

代理類型REMOTE_ADDRHTTP_VIAHTTP_X_FORWARDED_FOR匿名程度
無代理 用戶真實 IP 無/不顯示 無/不顯示 完全透明
透明代理
(Transparent Proxies)
最後一層代理 IP 代理伺服器 IP 用戶真實 IP
(多層時以逗號分隔)
低 - 仍傳遞真實 IP
普通匿名代理
(Anonymous Proxies)
最後一層代理 IP 代理伺服器 IP 代理伺服器 IP
(隱藏真實 IP,但暴露使用代理)
中 - 隱藏 IP 但暴露代理行為
欺騙性代理
(Distorting Proxies)
代理伺服器 IP 代理伺服器 IP 隨機偽造 IP 中 - 提供假 IP 欺騙目標
高匿名代理
(High Anonymity / Elite Proxies)
代理伺服器 IP 無/不顯示 無/不顯示 高 - 完全偽裝為代理伺服器直連

關鍵觀察

  • HTTP_X_FORWARDED_FOR 可能包含多個 IP(逗號分隔),格式為:client, proxy1, proxy2,**最左側通常為最原始客戶端 IP**
  • HTTP_VIA 記錄代理伺服器資訊,可用於偵測是否經過代理
  • HTTP_CLIENT_IP 非標準標頭,部分代理會設定,可作為備選參考
  • 所有標頭**皆可被偽造**,不可盲目信任

實用的 PHP 取得真實 IP 函式

版本一:基本邏輯(教學用)

<?php
function getIp() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    } else {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    return $ip;
}
?>

版本二:進階防禦版(生產環境建議)

<?php
/**
 * 取得客戶端真實 IP(增強版)
 * 
 * @param array $trustedProxies 受信任的代理 IP/CIDR 陣列(如 ['10.0.0.0/8', '192.168.0.0/16'])
 * @return string 客戶端 IP
 */
function getClientIp(array $trustedProxies = []) {
    // 1. 如果直接連線非受信任代理,直接回傳 REMOTE_ADDR
    $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    
    if (!empty($trustedProxies) && !isTrustedProxy($remoteAddr, $trustedProxies)) {
        return $remoteAddr;
    }
    
    // 2. 依優先順序檢查標頭
    $headers = [
        'HTTP_CLIENT_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_FORWARDED',
        'HTTP_X_CLUSTER_CLIENT_IP',
        'HTTP_FORWARDED_FOR',
        'HTTP_FORWARDED',
        'REMOTE_ADDR'
    ];
    
    foreach ($headers as $header) {
        if (empty($_SERVER[$header])) continue;
        
        $ips = array_map('trim', explode(',', $_SERVER[$header]));
        
        // 從左至右找第一個非私有/保留 IP
        foreach ($ips as $ip) {
            if (isValidPublicIp($ip)) {
                return $ip;
            }
        }
    }
    
    return $remoteAddr;
}

/** 判斷是否為受信任代理 */
function isTrustedProxy($ip, $trustedProxies) {
    foreach ($trustedProxies as $cidr) {
        if (ipInCidr($ip, $cidr)) return true;
    }
    return false;
}

/** CIDR 比對 */
function ipInCidr($ip, $cidr) {
    list($subnet, $mask) = explode('/', $cidr);
    $subnet = ip2long($subnet);
    $ipLong = ip2long($ip);
    $mask = -1 << (32 - (int)$mask);
    return ($ipLong & $mask) === ($subnet & $mask);
}

/** 驗證是否為合法公開 IP */
function isValidPublicIp($ip) {
    if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
        return false;
    }
    return true;
}
?>

實際部署架構下的最佳實踐

1. Nginx 反向代理正確設定

# nginx.conf
location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

2. PHP 應用層信任設定

<?php
// 僅信任內部網路代理(例如 10.0.0.0/8、172.16.0.0/12)
$trustedProxies = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
$realIp = getClientIp($trustedProxies);
?>

3. CDN 廠商特殊標頭處理

CDN 廠商真實 IP 標頭
CloudflareCF-Connecting-IP
AkamaiTrue-Client-IP
AWS CloudFrontX-Forwarded-For (第一個)
FastlyFastly-Client-IP
CloudFront + ALBX-Forwarded-For 需解析多層

建議在函式中優先檢查廠商專用標頭。

常見誤區與安全風險

  • ❌ 直接信任 HTTP_X_FORWARDED_FOR:惡意用戶可偽造標頭繞過 IP 封鎖、竄改日誌、進行暴力破解
  • ❌ 不驗證 IP 格式:導致資料庫儲存異常或後續邏輯錯誤
  • ❌ 忽略 IPv6:現代環境需同時支援 IPv4/IPv6
  • ✅ 建立受信任代理白名單:只解析來自已知基礎設施的標頭
  • ✅ 記錄完整標頭鏈:审計日誌保留 X-Forwarded-For 完整內容供事後分析
  • ✅ 使用成熟函式庫:如 Symfony HttpFoundation 的 Request::getClientIp()、Laravel 的 $request->ip() 已內建完善處理

總結

取得真實 IP 不是單純讀取某個變數,而是**基於網路拓撲的信任鏈驗證**。核心原則:

  1. 明確知道請求經過哪些代理層
  2. 只信任已知、可控制的代理層傳遞的標頭
  3. 解析 X-Forwarded-For 時從左向右找第一個非內部 IP
  4. 搭配應用層防火牆(WAF)與日誌審計機制

正確處理 IP 識別,是帳號安全、風控分析、個性化服務的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved