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

PHPBB IP 封鎖機制深度解析與自訂實作指南

     編輯:PHP 綜合知識

PHPBB 使用者/IP/Email 封鎖系統核心程式碼解析

PHPBB 作為經典論壇系統,其封鎖管理模組(admin_user_ban.php)展示了完整的使用者存取控制實作。本文深度解析其核心邏輯,並提供現代化重構參考。

核心功能架構

封鎖系統支援三種封鎖類型:

  • 使用者封鎖:依用戶 ID 封鎖特定帳號
  • IP 封鎖:支援單一 IP、IP 範圍(CIDR/範圍格式)、主機名稱解析
  • Email 封鎖:支援完整信箱、網域通配符(*@domain.com)

關鍵程式碼段解析

IP 範圍解析與展開演算法

支援 192.168.1.1-192.168.1.254 範圍格式,自動展開為離散 IP 列表:

<?php
function parseIpRange($ipRangeStr) {
    $ipList = [];
    $ranges = explode(',', $ipRangeStr);
    
    foreach ($ranges as $range) {
        $range = trim($range);
        
        // 格式 1:IP 範圍 192.168.1.1-192.168.1.254
        if (preg_match(
            '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s*-\s*(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/',
            $range, $m
        )) {
            $ipList = array_merge($ipList, expandIpRange(
                [$m[1], $m[2], $m[3], $m[4]],
                [$m[5], $m[6], $m[7], $m[8]]
            ));
        }
        // 格式 2:主機名稱 example.com
        elseif (preg_match('/^([\w\-_]+\.?){2,}$/i', $range)) {
            $ips = gethostbynamel($range);
            if ($ips) $ipList = array_merge($ipList, $ips);
        }
        // 格式 3:通配符 192.168.*.*
        elseif (preg_match('/^(\d{1,3})\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/', $range, $m)) {
            $ip = str_replace('*', '255', $range);
            $ipList[] = encodeIp($ip);
        }
    }
    return $ipList;
}

function expandIpRange($start, $end) {
    $ips = [];
    // 四層巢狀迴圈展開 IP 範圍(原始碼邏輯)
    for ($o1 = $start[0]; $o1 <= $end[0]; $o1++) {
        $o2Start = ($o1 == $start[0]) ? $start[1] : 0;
        $o2End = ($o1 < $end[0]) ? 254 : $end[1];
        for ($o2 = $o2Start; $o2 <= $o2End; $o2++) {
            $o3Start = ($o1 == $start[0] && $o2 == $start[1]) ? $start[2] : 0;
            $o3End = ($o1 < $end[0] || $o2 < $o2End) ? 254 : $end[2];
            for ($o3 = $o3Start; $o3 <= $o3End; $o3++) {
                $o4Start = ($o1 == $start[0] && $o2 == $start[1] && $o3 == $start[2]) ? $start[3] : 0;
                $o4End = ($o1 < $end[0] || $o2 < $o2End || $o3 < $o3End) ? 254 : $end[3];
                for ($o4 = $o4Start; $o4 <= $o4End; $o4++) {
                    $ips[] = encodeIp("$o1.$o2.$o3.$o4");
                }
            }
        }
    }
    return $ips;
}

// IP 編碼函數(PHPBB 專用格式)
function encodeIp($ip) {
    $parts = explode('.', $ip);
    return sprintf('%02x%02x%02x%02x', $parts[0], $parts[1], $parts[2], $parts[3]);
}
?>

封鎖寫入與 Session 即時生效

寫入資料庫後,立即刪除對應的 Session 強制登出:

<?php
function applyBans($userIds, $ipList, $emailList, $db) {
    $killSessionSql = '';
    
    // 1. 使用者封鎖
    foreach ($userIds as $uid) {
        if (!isAlreadyBanned('ban_userid', $uid, $db)) {
            $db->query("INSERT INTO banlist (ban_userid) VALUES ($uid)");
            $killSessionSql .= " OR session_user_id = $uid";
        }
    }
    
    // 2. IP 封鎖
    foreach ($ipList as $ip) {
        if (!isAlreadyBanned('ban_ip', $ip, $db)) {
            $db->query("INSERT INTO banlist (ban_ip) VALUES ('$ip')");
            // 建立 Session 清理條件
            if (preg_match('/(ff\.)|(\.ff)/i', chunk_split($ip, 2, '.'))) {
                // 含通配符的 IP
                $pattern = str_replace('.', '', preg_replace('/(ff\.)|(\.ff)/i', '%', chunk_split($ip, 2, '.')));
                $killSessionSql .= " OR session_ip LIKE '$pattern'";
            } else {
                $killSessionSql .= " OR session_ip = '$ip'";
            }
        }
    }
    
    // 3. Email 封鎖
    foreach ($emailList as $email) {
        if (!isAlreadyBanned('ban_email', $email, $db)) {
            $db->query("INSERT INTO banlist (ban_email) VALUES ('" . $db->escape($email) . "')");
        }
    }
    
    // 4. 即時清理 Session
    if ($killSessionSql) {
        $db->query("DELETE FROM sessions WHERE " . substr($killSessionSql, 4));
    }
}

function isAlreadyBanned($field, $value, $db) {
    $result = $db->query("SELECT 1 FROM banlist WHERE $field = " . $db->escape($value));
    return $db->num_rows($result) > 0;
}
?>

解封機制

<?php
function removeBans($banIds, $db) {
    if (empty($banIds)) return;
    $placeholders = implode(',', array_fill(0, count($banIds), '?'));
    $stmt = $db->prepare("DELETE FROM banlist WHERE ban_id IN ($placeholders)");
    $stmt->execute($banIds);
}
?>

現代化重構建議

資料庫結構優化

CREATE TABLE `banlist` (
    `ban_id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    `ban_type` ENUM('user', 'ip', 'email') NOT NULL,
    `ban_value` VARCHAR(255) NOT NULL,  -- 統一儲存值
    `ban_reason` TEXT,
    `ban_created_by` INT UNSIGNED,
    `ban_created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    `ban_expires_at` TIMESTAMP NULL,  -- 支援臨時封鎖
    INDEX `idx_type_value` (`ban_type`, `ban_value`),
    INDEX `idx_expires` (`ban_expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

使用 CIDR 標準儲存 IP

<?php
// 使用 PHP 內建 filter_var 驗證與標準化
function normalizeIp($input) {
    // 單一 IP
    if (filter_var($input, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
        return $input . '/32';
    }
    // CIDR 格式
    if (preg_match('/^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/', $input)) {
        return $input;
    }
    // 範圍格式轉 CIDR(需額外函式庫支援)
    // ...
    return false;
}

// 檢查 IP 是否在封鎖列表中
function isIpBanned($clientIp, $pdo) {
    $stmt = $pdo->query("SELECT ban_value FROM banlist WHERE ban_type = 'ip'");
    while ($row = $stmt->fetch()) {
        if (ipInCidr($clientIp, $row['ban_value'])) return true;
    }
    return false;
}

function ipInCidr($ip, $cidr) {
    list($subnet, $mask) = explode('/', $cidr);
    $ipLong = ip2long($ip);
    $subnetLong = ip2long($subnet);
    $maskLong = -1 << (32 - (int)$mask);
    return ($ipLong & $maskLong) === ($subnetLong & $maskLong);
}
?>

整合 PSR-7/15 中介軟體

<?php
class BanMiddleware {
    public function __construct(private PDO $pdo) {}
    
    public function process(Request $request, RequestHandler $handler): Response {
        $clientIp = $this->getClientIp($request);
        $userId = $request->getAttribute('user_id') ?? null;
        
        if ($this->isBanned($clientIp, $userId)) {
            return new Response(403, [], '您的存取已被封鎖');
        }
        
        return $handler->handle($request);
    }
    
    private function getClientIp(Request $request): string {
        // 支援代理標頭
        $headers = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];
        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                $ips = explode(',', $_SERVER[$header]);
                return trim($ips[0]);
            }
        }
        return '0.0.0.0';
    }
    
    private function isBanned(string $ip, ?int $userId): bool {
        $stmt = $this->pdo->prepare("
            SELECT 1 FROM banlist 
            WHERE (ban_type = 'ip' AND ? LIKE CONCAT(ban_value, '%'))
               OR (ban_type = 'user' AND ban_value = ?)
               AND (ban_expires_at IS NULL OR ban_expires_at > NOW())
        ");
        $stmt->execute([$ip, $userId]);
        return $stmt->fetchColumn() > 0;
    }
}
?>

安全性最佳實踐

  1. 輸入驗證:嚴格驗證 IP 格式、Email 格式、用戶 ID 存在性
  2. 權限控制:僅管理員可操作封鎖,記錄操作審計日誌
  3. 防禦繞過:支援 IPv6、代理標頭驗證、Session 綁定 IP
  4. 自動清理:定期清理過期封鎖記錄(ban_expires_at)
  5. 白名單機制:提供管理員/關鍵 IP 白名單防止誤封

結語

PHPBB 的封鎖機制雖然年代久遠,但其核心邏輯(IP 範圍展開、Session 即時失效、多類型封鎖)仍具參考價值。現代系統建議採用 CIDR 標準儲存 IP、整合中介軟體架構、支援臨時封鎖與自動過期,並搭配完整的審計追蹤與白名單保護機制。

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