PHP项目如何配置站点访问黑白名单?

wen PHP项目 57

本文目录导读:

PHP项目如何配置站点访问黑白名单?

  1. 方法一:服务器层面(Nginx / Apache)【最推荐】
  2. 方法二:PHP 框架中间件/路由层(Laravel / ThinkPHP / Symfony)
  3. 方法三:Nginx 动态黑名单(基于 Map / Geographic 模块)
  4. 注意事项
  5. 总结 - 你该用哪种?

在 PHP 项目中配置访问黑白名单,通常可以从服务器层面(最推荐)、框架层面代码层面来实现,以下是几种常见的安全配置方法,由优到次排列:


服务器层面(Nginx / Apache)【最推荐】

这种方法效率最高,不在 PHP 层面消耗资源,且不易被绕过。

Nginx 配置黑白名单

serverlocation 块中添加 allowdeny 指令。

示例:仅允许内网 + 特定 IP,禁止其他所有 IP(白名单模式)

server {
    listen 80;
    server_name yourdomain.com;
    # 允许内网段
    allow 192.168.1.0/24;
    # 允许某个特定外网 IP
    allow 8.8.8.8;
    # 允许公网某个 IP 段
    allow 203.0.113.0/24;
    # 拒绝其他所有
    deny all;
    # ... 其余配置
    location / {
        try_files $uri /index.php?$args;
    }
}

示例:禁止恶意 IP(黑名单模式)

server {
    # 黑名单 IP(可以放在 http、server 或 location 块)
    deny 192.0.2.1;
    deny 198.51.100.0/24;
    # 允许其他所有
    allow all;
    # ... 其余配置
}

提示deny all 是白名单模式,allow all 是黑名单模式。

Apache 配置黑白名单

通过 .htaccess 或主配置文件(httpd.conf / apache2.conf)的 <RequireAll>RequireIp 指令(Apache 2.4+)。

白名单(仅允许特定 IP)

<Directory /var/www/html>
    <RequireAll>
        Require ip 192.168.1.0/24
        Require ip 8.8.8.8
        Require all denied
    </RequireAll>
</Directory>

黑名单(禁止特定 IP)

<Directory /var/www/html>
    <RequireAll>
        Require ip not 192.0.2.1
        Require ip not 198.51.100.0/24
        Require all granted
    </RequireAll>
</Directory>

PHP 框架中间件/路由层(Laravel / ThinkPHP / Symfony)

如果你希望基于业务逻辑(例如某个 API 只允许白名单 IP 调用),或者需要动态管理名单(比如从数据库读取),可以在框架层实现。

Laravel 中间件示例

创建一个中间件 IpMiddleware,通过 app/Http/Middleware/IpMiddleware.php 实现:

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class IpMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        // 从数据库或配置中读取黑白名单
        $whitelistIps = ['192.168.1.1', '10.0.0.0/24']; // 支持 CIDR
        $clientIp = $request->ip();
        if (!$this->ipInList($clientIp, $whitelistIps)) {
            abort(403, 'Forbidden: IP not allowed');
        }
        return $next($request);
    }
    private function ipInList($ip, $list)
    {
        foreach ($list as $allowedIp) {
            if (strpos($allowedIp, '/') !== false) {
                // 处理 CIDR 子网
                if ($this->ipInCIDR($ip, $allowedIp)) return true;
            } else {
                if ($ip === $allowedIp) return true;
            }
        }
        return false;
    }
    private function ipInCIDR($ip, $cidr)
    {
        list($subnet, $bits) = explode('/', $cidr);
        $ip = ip2long($ip);
        $subnet = ip2long($subnet);
        $mask = -1 << (32 - $bits);
        return ($ip & $mask) === ($subnet & $mask);
    }
}

然后在 app/Http/Kernel.php 中注册为全局中间件或路由中间件。

简易 PHP 原生过滤(没有框架)

在入口文件(如 index.php)顶部加入:

$whitelist = ['192.168.1.10', '10.0.0.%']; // 支持通配符
$clientIp = $_SERVER['REMOTE_ADDR'] ?? '';
$allowed = false;
foreach ($whitelist as $ip) {
    if (strpos($ip, '%') !== false) {
        $pattern = str_replace('%', '\d+', $ip);
        if (preg_match("/^$pattern$/", $clientIp)) {
            $allowed = true;
            break;
        }
    } elseif ($ip === $clientIp) {
        $allowed = true;
        break;
    }
}
if (!$allowed) {
    http_response_code(403);
    die('Access Denied');
}

Nginx 动态黑名单(基于 Map / Geographic 模块)

对于大规模黑名单(如根据 GeoIP 禁止国家),可以使用 Nginx 的 geo 模块或 geoip2 模块,适合运维场景。

示例:根据国家禁止访问

# 需要安装 ngx_http_geoip_module 或 ngx_http_geoip2_module
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $allow_country {
    default 0;
    CN 1;
    US 1;
}
server {
    if ($allow_country = 0) {
        return 403;
    }
    # ... 其余配置
}

注意事项

  1. 优先级:服务器层面(Nginx/Apache)先于 PHP 代码运行,如果服务器配置了 deny all,PHP 代码根本不会接收到请求。
  2. 反向代理场景:PHP 服务器在 Nginx 后面(如使用 Nginx 代理到 PHP-FPM),$_SERVER['REMOTE_ADDR'] 获取的是 Nginx 的内网 IP,需要在 Nginx 中设置 proxy_set_header X-Forwarded-For $remote_addr;,并在 PHP 中读取 $_SERVER['HTTP_X_FORWARDED_FOR'](注意伪造 XFF 的风险)。
  3. 动态与静态:静态规则(IP 段很少变化)放服务器层面,需要频繁更新(如根据数据库记录动态封禁 IP)则用 PHP 代码或防火墙脚本。
  4. 性能:服务器层面是毫秒级过滤,PHP 层面每次请求需执行代码,高并发时建议优先用服务器或防火墙(如 fail2ban + iptables)。

- 你该用哪种?

场景 推荐方案
后台管理只允许公司内网访问 Nginx/Apache 白名单
需要根据数据库黑名单实时封禁 IP PHP 中间件或入口脚本
跨国业务要封禁某些国家 Nginx GeoIP + map 模块,或防火墙策略
临时屏蔽某个 IP 的攻击 Nginx 直接在配置加 deny(reload 生效),或用 iptables/fail2ban

根据你的实际环境(是否有 Nginx?是否用框架?名单是固定的还是动态变化的?)选择合适的层级即可。

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