如何为PHP项目配置黑白名单?

wen PHP项目 1

本文目录导读:

如何为PHP项目配置黑白名单?

  1. 基于IP地址的黑白名单(最常用)
  2. 基于数据库的动态黑白名单
  3. 基于用户认证的黑白名单
  4. 基于Nginx/Apache的服务器级别控制(性能最优)
  5. 综合最佳实践
  6. 关键注意事项

为PHP项目配置黑白名单可以基于IP地址、用户ID、请求来源、API密钥等不同维度,下面介绍几种常见且可落地的配置方法,按复杂度从低到高排列:


基于IP地址的黑白名单(最常用)

简单实现(硬编码或配置文件)

config/access.php

<?php
return [
    // IP白名单
    'whitelist' => [
        '192.168.1.100',
        '10.0.0.0/24',      // 支持CIDR
        '172.16.0.0-172.16.0.255'  // 支持范围
    ],
    // IP黑名单
    'blacklist' => [
        '203.0.113.0/24',
        '198.51.100.50'
    ],
    'mode' => 'whitelist' // 或 'blacklist'
];

中间件/入口检查

src/Middleware/AccessControl.php

<?php
namespace App\Middleware;
class AccessControl
{
    private $config;
    public function __construct(array $config)
    {
        $this->config = $config;
    }
    public function handle($request, $next)
    {
        $ip = $request->getClientIp();
        // 检查黑名单(黑名单优先于白名单)
        if ($this->isBlacklisted($ip)) {
            http_response_code(403);
            exit('Access denied');
        }
        // 如果是白名单模式,检查是否在白名单中
        if ($this->config['mode'] === 'whitelist') {
            if (!$this->isWhitelisted($ip)) {
                http_response_code(403);
                exit('Access denied');
            }
        }
        return $next($request);
    }
    private function isWhitelisted($ip)
    {
        return $this->matchIpList($ip, $this->config['whitelist']);
    }
    private function isBlacklisted($ip)
    {
        return $this->matchIpList($ip, $this->config['blacklist']);
    }
    private function matchIpList($ip, $list)
    {
        foreach ($list as $rule) {
            if ($this->ipMatches($ip, $rule)) {
                return true;
            }
        }
        return false;
    }
    private function ipMatches($ip, $rule)
    {
        if (strpos($rule, '/')) {
            // CIDR 匹配
            list($subnet, $bits) = explode('/', $rule);
            $ip = ip2long($ip);
            $subnet = ip2long($subnet);
            $mask = -1 << (32 - $bits);
            return ($ip & $mask) === ($subnet & $mask);
        } elseif (strpos($rule, '-')) {
            // 范围匹配
            list($start, $end) = explode('-', $rule);
            $ip = ip2long($ip);
            return $ip >= ip2long($start) && $ip <= ip2long($end);
        } else {
            // 精确匹配
            return $ip === $rule;
        }
    }
}

使用示例(入口文件)

public/index.php

<?php
$accessConfig = require '../config/access.php';
$accessControl = new \App\Middleware\AccessControl($accessConfig);
// 假设使用某个框架的Request对象
$accessControl->handle($request, function($req) {
    // 正常业务逻辑
    echo "Welcome!";
});

基于数据库的动态黑白名单

适合需要后台管理、频繁更新规则的场景。

数据库结构

CREATE TABLE access_list (
    id INT AUTO_INCREMENT PRIMARY KEY,
    type ENUM('whitelist', 'blacklist') NOT NULL,
    value VARCHAR(255) NOT NULL,        -- IP、用户ID、域名等
    match_type ENUM('ip', 'user_id', 'domain', 'path') DEFAULT 'ip',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

PHP 查询与匹配

<?php
class DynamicAccessControl
{
    private $db;
    public function __construct(PDO $db)
    {
        $this->db = $db;
    }
    public function check($type, $value)
    {
        // 先检查黑名单
        $stmt = $this->db->prepare(
            "SELECT COUNT(*) FROM access_list 
             WHERE type = 'blacklist' AND value = ? AND match_type = ?"
        );
        $stmt->execute([$value, $type]);
        if ($stmt->fetchColumn() > 0) {
            return false;
        }
        // 如果是白名单模式,再检查白名单
        $stmt = $this->db->prepare(
            "SELECT COUNT(*) FROM access_list 
             WHERE type = 'whitelist' AND value = ? AND match_type = ?"
        );
        $stmt->execute([$value, $type]);
        return $stmt->fetchColumn() > 0;
    }
}

基于用户认证的黑白名单

适用于需要按用户级别控制访问的场景。

<?php
class UserAccessControl
{
    const ROLE_WHITELIST = [
        'admin' => ['*'],                     // 管理员全部允许
        'editor' => ['/admin/*', '/api/*'],   // 编辑可访问特定路径
        'user' => ['/profile', '/content/*']  // 普通用户
    ];
    const USER_BLACKLIST = [
        1001, 1005, 2003  // 被封禁的用户ID
    ];
    public function check($userId, $userRole, $requestPath)
    {
        // 黑名单用户
        if (in_array($userId, self::USER_BLACKLIST)) {
            http_response_code(403);
            exit('Your account has been suspended');
        }
        // 白名单路径检查
        if (!isset(self::ROLE_WHITELIST[$userRole])) {
            return false;
        }
        $allowedPaths = self::ROLE_WHITELIST[$userRole];
        foreach ($allowedPaths as $pattern) {
            if (fnmatch($pattern, $requestPath)) {
                return true;
            }
        }
        return false;
    }
}

基于Nginx/Apache的服务器级别控制(性能最优)

Nginx 配置

# IP白名单
location /admin {
    allow 192.168.1.0/24;
    allow 10.0.0.1;
    deny all;
}
# 全局黑名单
location / {
    deny 203.0.113.0/24;
    deny 198.51.100.50;
    # 其他正常配置
}

Apache .htaccess

# 白名单
<RequireAny>
    Require ip 192.168.1.0/24
    Require ip 10.0.0.1
</RequireAny>
# 黑名单
<RequireAll>
    Require all granted
    Require not ip 203.0.113.0/24
    Require not ip 198.51.100.50
</RequireAll>

综合最佳实践

<?php
class AdvancedAccessControl
{
    private $cache = [];
    private $db;
    public function __construct(PDO $db)
    {
        $this->db = $db;
    }
    public function checkRequest($request)
    {
        // 1. 快速缓存检查(Redis/Memcached)
        $cacheKey = 'acl:' . $request->getClientIp();
        if (isset($this->cache[$cacheKey])) {
            return $this->cache[$cacheKey];
        }
        // 2. 多维度检查
        $checkResult = $this->multiDimensionalCheck($request);
        // 3. 缓存结果(5分钟)
        $this->cache[$cacheKey] = $checkResult;
        // 4. 也可以写入Redis
        // Redis::setex($cacheKey, 300, (int)$checkResult);
        return $checkResult;
    }
    private function multiDimensionalCheck($request)
    {
        $ip = $request->getClientIp();
        $userId = $request->getUserId();
        $path = $request->getPath();
        $userAgent = $request->getUserAgent();
        $referer = $request->getReferer();
        // 黑名单优先级最高
        if ($this->isInBlacklist($ip, $userId, $path)) {
            return false;
        }
        // 白名单模式检查
        if (ACCESS_MODE === 'whitelist') {
            return $this->isInWhitelist($ip, $userId, $path);
        }
        // 混合模式:API路径同时检查IP和UserID
        if (strpos($path, '/api/') === 0) {
            return $this->checkApiAccess($ip, $userId);
        }
        return true; // 默认允许
    }
    private function checkApiAccess($ip, $userId)
    {
        // 内部API只能由特定IP访问
        if (strpos($path, '/api/internal/') === 0) {
            return in_array($ip, INTERNAL_IPS);
        }
        // 公开API需要token中携带的白名单状态
        return true;
    }
}

关键注意事项

方面 建议
性能 生产环境优先用Nginx/Apache层控制,PHP层做次要校验
缓存 每次请求都查数据库会严重降低性能,建议用Redis缓存规则
日志 所有被拒绝的请求应当记录日志(IP、时间、路径、原因)
监控 黑名单命中率异常升高可能是攻击,建议设置告警
更新 动态列表变动后要有机制清除缓存(如Redis Pub/Sub)

简单场景:用方法1(配置文件+中间件)
需要管理后台:用方法2(数据库动态列表)
高并发:用方法4(Nginx层)+ PHP层二次校验
复杂权限:综合方法3和方法5

选择哪种方式取决于你的项目规模、并发量、以及是否需要动态管理规则。

抱歉,评论功能暂时关闭!