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

主流 CMS/CDN 環境獲取真實客戶端 IP 完整方案 (PHP/ASP/ASP.NET)

     編輯:PHP 相關知識

核心原理

CDN/反向代理轉發請求時,原始客戶端 IP 通過以下 Header 傳遞 (優先級遞減):

  1. HTTP_CLIENT_IP (部分代理)
  2. HTTP_X_FORWARDED_FOR (標準,格式:client, proxy1, proxy2)
  3. REMOTE_ADDR (直接連接或最後一級代理 IP)

關鍵:X-Forwarded-For 可偽造,**必須**從左向右取第一個**非內網/保留地址**的 IP。

通用 PHP 獲取真實 IP 函數

<?php
function getRealIp(): string {
    $ip = false;

    // 1. HTTP_CLIENT_IP
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    }

    // 2. HTTP_X_FORWARDED_FOR (可能包含多個 IP)
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = array_map('trim', explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
        if ($ip) array_unshift($ips, $ip); // 合併優先級

        foreach ($ips as $candidate) {
            // 過濾私有/保留地址段
            if (!preg_match('/^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/', $candidate)) {
                $ip = $candidate;
                break;
            }
        }
    }

    // 3. 回退 REMOTE_ADDR
    return $ip ?: ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0');
}

// 使用
$realIp = getRealIp();
?>

主流 CMS 修改指南

Supesite / UCenter

文件:include/main.inc.php

// 替換原有獲取 IP 邏輯為:
$_SGLOBAL['onlineip'] = getRealIp();

Discuz! (X 系列)

文件:source/class/discuz/discuz_application.php 或 config/config_global.php (新版)

// 修改 $_G['clientip'] 獲取邏輯
$_G['clientip'] = getRealIp();

或在 config/config_global.php 直接定義:

$_config['security']['ipcheck'] = 1; // 開啟 IP 檢查
// 配合插件或修改核心函數 getonlineip()

EmpireCMS (帝國 CMS)

文件:e/class/connect.php 中的 egetip() 函數

function egetip() {
    return getRealIp(); // 替換原有實現
}

Discuz! 註冊 IP 與邀請碼 IP 一致問題

現象:後台開啟「註冊 IP 與邀請碼擁有者 IP 不一致則禁止」導致誤攔截。

方案 A:註釋攔截邏輯 (source/module/member/member_register.php)

// showmessage('register_invite_iperror');

方案 B:修正 IP 獲取 (同上)

ASP / ASP.NET 獲取真實 IP

ASP (VBScript) 函數

<%
Function CheckIP(ipStr)
    Dim regEx
    Set regEx = New RegExp
    regEx.Pattern = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"
    regEx.Global = False
    regEx.IgnoreCase = False
    CheckIP = regEx.Test(ipStr)
    Set regEx = Nothing
End Function

Function GetClientIP()
    Dim clientIP, arr, i
    If CheckIP(Request.ServerVariables("HTTP_CLIENT_IP")) Then
        GetClientIP = Request.ServerVariables("HTTP_CLIENT_IP")
        Exit Function
    End If
    arr = Split(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ",")
    For i = 0 To UBound(arr)
        clientIP = Trim(arr(i))
        If CheckIP(clientIP) Then
            GetClientIP = clientIP
            Exit Function
        End If
    Next
    GetClientIP = Request.ServerVariables("REMOTE_ADDR")
End Function
%>

ASP.NET (C#) 多種方法


// 1. 標準屬性 (受代理影響)
string ip1 = HttpContext.Current.Request.UserHostAddress;

// 2. ServerVariables
string ip2 = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

// 3. DNS 解析 (獲取服務器自身 IP,非客戶端)
string hostName = System.Net.Dns.GetHostName();
string ip3 = System.Net.Dns.GetHostAddresses(hostName)[0].ToString();

// 4. 無視代理獲取真實 IP (X-Forwarded-For)
string ip4 = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

// 5. 綜合判斷 (推薦)
public string GetRealClientIP() {
    var request = HttpContext.Current.Request;
    if (request.ServerVariables["HTTP_VIA"] != null) { // 經過代理
        string xff = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
        if (!string.IsNullOrEmpty(xff)) {
            // 取第一個非內網 IP
            foreach (var ip in xff.Split(',')) {
                ip = ip.Trim();
                if (IsPublicIP(ip)) return ip;
            }
        }
    }
    return request.ServerVariables["REMOTE_ADDR"];
}

private bool IsPublicIP(string ip) {
    // 簡單私有段過濾
    return !Regex.IsMatch(ip, @"^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)");
}

客戶端 JS 獲取 IP (需服務端配合輸出)

<script>
var ip = '<!--#echo var="REMOTE_ADDR"-->'; // 需 SSI 支持
alert("Your IP: " + ip);

// ActiveX (僅 IE、需用戶允許、極不推薦)
function GetLocalIPAddress() {
    try {
        var obj = new ActiveXObject("rcbdyctl.Setting");
        return obj.GetIPAddress;
    } catch(e) { return ""; }
}
</script>

印度 Maulik Patel 方案 (ASP.NET 精簡版)

string ip;
if (Context.Request.ServerVariables["HTTP_VIA"] != null) {
    ip = Context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();
} else {
    ip = Context.Request.ServerVariables["REMOTE_ADDR"].ToString();
}

重要提醒

  • X-Forwarded-For 可偽造:客戶端可任意發送此 Header。必須**只信任已知代理層轉發的 Header** (Nginx proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 覆蓋客戶端值)。
  • 多層代理:Nginx → CDN → WAF,確保每層正確 proxy_add_x_forwarded_for 追加而非覆蓋。
  • IPv6 支持:正則需兼容 IPv6 格式,MySQL 存儲建議 VARBINARY(16) 或 VARCHAR(45)。
  • Header 大小寫:PHP $_SERVER 鍵為大寫 (HTTP_X_FORWARDED_FOR),Nginx 轉發時設置 proxy_set_header X-Forwarded-For ... (大小寫不敏感)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved