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

PHP IP 黑/白名單驗證實作:支援 CIDR 區段與類別封鎖完整範例

     編輯:PHP 綜合知識

前言

許多系統需依 IP 限制存取權限(如校園內網專屬新聞、後台管理介面)。本文提供兩種實作方案:

  1. 簡易版:以文字檔 ip.txt 存放允許 IP/網段,支援 C 類網段(如 192.168、211.67.188)與單一 IP。
  2. 進階類別版:封裝為 block_ip 類別,支援 IP-IP 範圍、* 萬用字元、自動偵測代理 Header(HTTP_X_FORWARDED_FOR、HTTP_CLIENT_IP)並整合正則比對。

方案一:簡易文字檔比對(適合小型專案)

ip.txt 範例內容

192.168
211.67.188
211.67.191.25

核心驗證程式碼

<?php
$ip = $_SERVER['REMOTE_ADDR'];
// 讀取 ip.txt,移除換行符號
$ipArray = preg_replace("#\r\n?|\n#", "", file('ip.txt'));

foreach ($ipArray as $ipTest) {
    // substr_count 判斷客戶端 IP 是否包含名單片段
    if (substr_count($ip, $ipTest) !== 0) {
        echo "ok"; // 允許存取,執行後續邏輯
        exit;
    }
}
// 不在名單中 → 拒絕或導向錯誤頁
echo "Access Denied";
?>

原理:利用字串包含關係比對,192.168 即可涵蓋 192.168.x.x 整個 C 類網段,簡單直觀但無法處理複雜 CIDR(如 /24、/16)或 IP 範圍(192.168.1.10-192.168.1.20)。

方案二:block_ip 類別(生產環境建議)

功能特色

  • 支援格式:192.168.1.1(單一)、210.10.2.1-20(範圍)、222.34.4.*(萬用字元)
  • 自動解析 $_SERVER 取得真實客戶端 IP(穿透反向代理/CDN)
  • 將規則編譯為正則運算式,效能優於逐字串比對
  • 靜態快取機制避免重複編譯

完整類別程式碼

<?php
class block_ip {
    // 封鎖規則陣列,可從資料庫/設定檔載入
    var $Block_ip = array("192.168.1.1", "210.10.2.1-20", "222.34.4.*");

    function __construct() {}
    function __destruct() {}

    // 將單一規則轉為正則片段
    private function makePregIP($str) {
        $preg_limit = '';
        if (strstr($str, "-")) {
            $aIP = explode(".", $str);
            foreach ($aIP as $k => $v) {
                if (!strstr($v, "-")) {
                    $preg_limit .= $this->makePregIP($v);
                } else {
                    $aipNum = explode("-", $v);
                    $preg = '';
                    for ($i = $aipNum[0]; $i <= $aipNum[1]; $i++) {
                        $preg .= $preg ? "|".$i : "[".$i;
                    }
                    $preg_limit .= (strrpos($preg_limit, ".") === strlen($preg_limit)-1) ? $preg."]" : ".".$preg."]";
                }
            }
        } else {
            $preg_limit .= $str.".";
        }
        return $preg_limit;
    }

    // 取得所有編譯後的正則規則
    private function getAllBlockIP() {
        $ip_list = [];
        if ($this->Block_ip) {
            foreach ($this->Block_ip as $k => $v) {
                $ipaddres = $this->makePregIP($v);
                $ip = str_ireplace(".", "\.", $ipaddres);
                $ip = str_replace("*", "[0-9]{1,3}", $ip);
                $ip_list[] = "/".$ip."/";
            }
        }
        return $ip_list;
    }

    // 取得客戶端真實 IP
    private function get_client_ip() {
        if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
            $ip = getenv("HTTP_CLIENT_IP");
        else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
            $ip = getenv("HTTP_X_FORWARDED_FOR");
        else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
            $ip = getenv("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;
    }

    // 公開檢查方法
    public function checkIP() {
        $iptable = $this->getAllBlockIP();
        $IsJoined = true;
        $Ip = trim($this->get_client_ip());

        if ($iptable) {
            foreach ($iptable as $value) {
                if (preg_match($value, $Ip)) {
                    $IsJoined = false;
                    break;
                }
            }
        }

        if (!$IsJoined) {
            header("HTTP/1.1 403 Forbidden");
            exit("IP Error: Access Denied");
        }
    }
}

// 使用方式
$oBlock_ip = new block_ip();
$oBlock_ip->checkIP();
// 通過後繼續執行正常業務邏輯
?>

部署建議與注意事項

  1. 信任代理 Header:若架構在 Nginx/Cloudflare 後方,務必在 Web Server 設定 real_ip_header,避免偽造 X-Forwarded-For。
  2. 效能優化:將編譯後的正則快取至 Redis/APCu,避免每請求重新 makePregIP。
  3. IPv6 支援:現有程式碼僅處理 IPv4,若需支援 IPv6 需擴充 makePregIP 邏輯。
  4. 白名單模式:將 $IsJoined 邏輯反轉即可實作「僅允許名單內 IP」。

結語

從簡易的 ip.txt 到功能完整的 block_ip 類別,開發者可依專案規模選擇適當方案。核心觀念在於:正確取得客戶端真實 IP + 靈活的規則表示法 + 高效能比對演算法。建議生產環境採用類別版並搭配快取機制,確保安全且不影響回應速度。

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