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

PHP 取得客戶端真實 IP 完整方案:穿透代理與負載平衡器

     編輯:PHP 相關知識

在反向代理、CDN、負載平衡器架構下,$_SERVER['REMOTE_ADDR'] 往往是代理伺服器 IP。本文提供完整的真實 IP 取得函數,支援多層代理鏈與 IPv6。

核心函數實作

<?php
function getClientIp(): string {
    $headers = [
        'HTTP_CLIENT_IP',           // 標準代理 Header
        'HTTP_X_FORWARDED_FOR',     // 最常見,可能包含多個 IP(逗號分隔)
        'HTTP_X_FORWARDED',         // 較少見
        'HTTP_X_CLUSTER_CLIENT_IP', // AWS ALB 等
        'HTTP_FORWARDED_FOR',       // RFC 7239
        'HTTP_FORWARDED',           // RFC 7239 標準格式
        'REMOTE_ADDR',              // 最後備援
    ];

    foreach ($headers as $header) {
        if (empty($_SERVER[$header])) continue;
        
        // X-Forwarded-For 可能包含多個 IP:client, proxy1, proxy2...
        $ips = array_map('trim', explode(',', $_SERVER[$header]));
        
        foreach ($ips as $ip) {
            $ip = trim($ip);
            if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                return $ip;
            }
        }
    }

    // 兜底:回傳 REMOTE_ADDR(可能為私有 IP)
    return $_SERVER['REMOTE_ADDR'] ?? 'unknown';
}
?>

進階版:支援信任代理清單

<?php
function getClientIpAdvanced(array $trustedProxies = []): string {
    // 預設信任私有網段代理
    $trustedProxies = array_merge([
        '10.0.0.0/8',
        '172.16.0.0/12',
        '192.168.0.0/16',
        '127.0.0.0/8',
        '::1/128',
        'fc00::/7',
    ], $trustedProxies);

    $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';

    // 如果直接連線非受信代理,直接回傳
    if (!ipInRanges($remoteAddr, $trustedProxies)) {
        return $remoteAddr;
    }

    // 從 Header 取得原始 IP
    $forwardedFor = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
    $ips = array_map('trim', explode(',', $forwardedFor));
    
    // 從右往左找第一個非信任代理 IP
    for ($i = count($ips) - 1; $i >= 0; $i--) {
        if (!ipInRanges($ips[$i], $trustedProxies)) {
            return $ips[$i];
        }
    }

    return $remoteAddr;
}

function ipInRanges(string $ip, array $cidrs): bool {
    foreach ($cidrs as $cidr) {
        if (ipInCidr($ip, $cidr)) return true;
    }
    return false;
}

function ipInCidr(string $ip, string $cidr): bool {
    if (strpos($cidr, '/') === false) return $ip === $cidr;
    list($rangeIp, $mask) = explode('/', $cidr);
    $mask = (int)$mask;
    
    if (strpos($ip, ':') !== false) { // IPv6
        $ipBin = inet_pton($ip);
        $rangeBin = inet_pton($rangeIp);
        for ($i = 0; $i < 16; $i++) {
            if ($mask <= 0) break;
            $bits = min(8, $mask);
            $mask -= $bits;
            $byteMask = (0xFF << (8 - $bits)) & 0xFF;
            if (($ipBin[$i] & $byteMask) !== ($rangeBin[$i] & $byteMask)) return false;
        }
        return true;
    } else { // IPv4
        $ipLong = ip2long($ip);
        $rangeLong = ip2long($rangeIp);
        $maskLong = -1 << (32 - $mask);
        return ($ipLong & $maskLong) === ($rangeLong & $maskLong);
    }
}
?>

框架整合範例

<?php
// Laravel: 設定 trusted proxies
// App\Http\Middleware\TrustProxies.php
protected $proxies = '*'; // 或具體 IP 陣列
protected $headers = Request::HEADER_X_FORWARDED_ALL;

// Symfony: framework.yaml
framework:
    trusted_proxies: '10.0.0.0/8,192.168.0.0/16'
    trusted_headers: ['x-forwarded-for', 'x-forwarded-host']

// 原生 PHP 中介層
function applyTrustedProxyMiddleware(array $trustedProxies = []): void {
    $_SERVER['CLIENT_IP'] = getClientIpAdvanced($trustedProxies);
}
?>

安全性關鍵點

⚠️ 絕不信任未驗證的 Header
X-Forwarded-For 可被客戶端偽造。必須確認 REMOTE_ADDR 為受信代理後,才採信 Header 值。

IPv6 支援檢查

<?php
// 測試 IPv6 解析
$testIps = [
    '2001:db8::1',
    '::ffff:192.168.1.1', // IPv4-mapped IPv6
    '::1',
];

foreach ($testIps as $ip) {
    echo "$ip => " . (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 'Valid' : 'Invalid') . "\n";
}
?>

正確取得客戶端 IP 是訪問控制、風控、日誌審計的基礎,務必根據實際網路拓撲調整信任代理清單。

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