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

PHP IP 封鎖函數:支援 CIDR、範圍、單一 IP 的完整實作

     編輯:PHP 相關知識

功能需求

  • 支援單一 IP:192.168.1.100
  • 支援 CIDR 區塊:192.168.1.0/24
  • 支援範圍格式:192.168.1.[10-20]
  • 支援萬用字元:192.168.*.*
  • 設定檔驅動,一行一條規則

核心實作

<?php
/**
 * 檢查 IP 是否符合規則
 * @param string $rule 規則字串
 * @param string $ip 待檢查 IP(預設自動取得 REMOTE_ADDR)
 * @return bool
 */
function matchIpRule(string $rule, string $ip = ''): bool {
    if ($ip === '') $ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
    if ($ip === '127.0.0.1') return true; // 本地永遠允許
    
    $rule = trim($rule);
    if ($rule === '') return false;
    
    // CIDR 格式:192.168.1.0/24
    if (preg_match('/^(\d{1,3}(?:\.\d{1,3}){3})\/(\d{1,2})$/', $rule, $m)) {
        $rangeIp = ip2long($m[1]);
        $mask = $m[2];
        $ipLong = ip2long($ip);
        $netmask = -1 << (32 - $mask);
        return ($ipLong & $netmask) === ($rangeIp & $netmask);
    }
    
    // 範圍格式:192.168.1.[10-20] 或 192.168.1.10-192.168.1.20
    if (preg_match('/^(\d{1,3}(?:\.\d{1,3}){3})\[(\d+)-(\d+)\]$/', $rule, $m)) {
        $base = $m[1];
        $start = (int)$m[2];
        $end = (int)$m[3];
        $ipParts = explode('.', $ip);
        $baseParts = explode('.', $base);
        // 前三段必須相同
        if ($ipParts[0] !== $baseParts[0] || $ipParts[1] !== $baseParts[1] || $ipParts[2] !== $baseParts[2]) {
            return false;
        }
        $lastOctet = (int)$ipParts[3];
        return $lastOctet >= $start && $lastOctet <= $end;
    }
    
    // 萬用字元:192.168.*.*
    if (strpos($rule, '*') !== false) {
        $pattern = '^' . str_replace(['.', '*'], ['\.', '\d{1,3}'], preg_quote($rule, '/')) . '$';
        return (bool)preg_match('/' . $pattern . '/', $ip);
    }
    
    // 精確比對
    return $rule === $ip;
}

/**
 * 從設定檔載入規則並檢查
 * @param string $configPath 設定檔路徑
 * @param string $ip 待檢查 IP
 * @return bool true=允許, false=封鎖
 */
function checkIpAccess(string $configPath, string $ip = ''): bool {
    if (!is_file($configPath)) return true; // 無設定檔預設允許
    
    $handle = fopen($configPath, 'r');
    if (!$handle) return true;
    
    while (($line = fgets($handle)) !== false) {
        $line = trim($line);
        // 跳過空行與註解
        if ($line === '' || $line[0] === '#') continue;
        // 移除行內註解
        if (($pos = strpos($line, '#')) !== false) $line = trim(substr($line, 0, $pos));
        
        if (matchIpRule($line, $ip)) {
            fclose($handle);
            return true; // 符合允許規則
        }
    }
    fclose($handle);
    return false; // 無符合規則,預設封鎖
}

設定檔範例 ip.allow

# 允許名單(白名單模式)
127.0.0.1
192.168.1.100          # 單一 IP
10.0.0.0/8             # 整個 A 類網段
172.16.0.0/12          # 私有 B 類網段
192.168.0.0/16         # 私有 C 類網段
192.168.1.[10-50]      # 範圍
203.45.*.*             # 萬用字元

整合至應用程式

<?php
// bootstrap.php 或 index.php 最頂端
require 'ip_filter.php';

$allowed = checkIpAccess(__DIR__ . '/config/ip.allow');
if (!$allowed) {
    http_response_code(403);
    die('您的 IP 已被封鎖,如有疑問請聯繫管理員。');
}

// 繼續正常執行...

黑名單模式變體

function checkIpBlocked(string $blocklistPath, string $ip = ''): bool {
    // 邏輯相反:符合規則即封鎖
    // 設定檔命名 ip.deny
    // 回傳 true 表示被封鎖
}

效能優化建議

  • 將 CIDR 規則預先轉換為 ['start' => long, 'end' => long] 陣列,避免每次請求重複計算。
  • 使用 APCu 或 Redis 快取編譯後的規則陣列。
  • 高流量系統建議移至 Nginx ngx_http_access_module 或防火牆層(iptables/ipset)處理。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved