Webman 防 CC 攻击方案
概述
防 CC 攻击分两层:边缘拦截(Nginx)+ 应用层检测(webman)。两层配合才能既挡得住、又误伤少。
整体架构
攻击流量
↓
[Nginx 限流] ← 入口挡掉 80% 的高频请求
↓
[webman 中间件] ← 行为分析 + 智能封禁
↓
[fail2ban] ← 服务器层兜底,iptables 直接拉黑
↓
正常请求 → 业务
一、Nginx 层拦截
在 nginx 的 http 段加限流规则:
nginx
# 定义限流规则(按 IP)
limit_req_zone $binary_remote_addr zone=cc_limit:10m rate=10r/s;
# 定义并发连接限制
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
# ...
# 全局限流:每秒 10 次请求,允许瞬时突发 20 个
limit_req zone=cc_limit burst=20 nodelay;
limit_req_status 503;
# 限制单个 IP 并发连接数
limit_conn conn_limit 20;
limit_conn_status 503;
}
参数说明:
| 参数 | 含义 |
|---|---|
rate=10r/s |
每秒允许 10 次请求 |
burst=20 |
允许瞬时突发 20 个请求进队列 |
nodelay |
队列满则直接返回 503,不等待 |
limit_conn 20 |
单 IP 最大 20 个并发连接 |
注意: 如果有多台 webman 服务器,nginx 层的限流是分散在每台 nginx 上的。此时建议同时部署 webman 中间件做统一限流。
二、webman 全局限流中间件
2.1 安装限流组件
bash
composer require webman/limiter
2.2 创建 CC 防护中间件
php
// app/middleware/CcProtect.php
<?php
namespace app\middleware;
use support\Request;
use support\limiter\Limiter;
use support\limiter\RateLimitException;
use Webman\RedisQueue\Redis;
class CcProtect
{
// 高频 IP 黑名单(Redis Set,TTL 10分钟)
private const BLACKLIST_KEY = 'cc:blacklist';
private const BAN_TTL = 600; // 封禁 10 分钟
// 记录请求的滑动窗口(用于行为分析)
private const WINDOW_KEY = 'cc:window:';
private const WINDOW_SIZE = 60; // 60 秒窗口
public function handle(Request $request, \Closure $next)
{
$ip = $this->getRealIp($request);
// 1. 检查是否在黑名单(直接拦截)
if ($this->isBlacklisted($ip)) {
return response('Service Unavailable', 503);
}
// 2. 全局限流(每秒最多 100 次 / IP)
try {
Limiter::check($ip, 100, 1);
} catch (RateLimitException $e) {
$this->addToBlacklist($ip);
return response('Service Unavailable', 503);
}
// 3. 行为检测(60秒内超过 500 次请求 → 加入黑名单)
$this->checkBehavior($ip);
// 4. 特殊路径限流
$this->checkSpecialPaths($request, $ip);
return $next($request);
}
private function getRealIp(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 isBlacklisted(string $ip): bool
{
return (bool) Redis::sIsMember(self::BLACKLIST_KEY, $ip);
}
private function addToBlacklist(string $ip): void
{
Redis::sAdd(self::BLACKLIST_KEY, $ip);
Redis::expire(self::BLACKLIST_KEY, self::BAN_TTL);
$log = sprintf(
"[%s] CC攻击拦截 IP: %s\n",
date('Y-m-d H:i:s'),
$ip
);
@file_put_contents(runtime()->get('path') . '/logs/cc_attack.log', $log, FILE_APPEND);
}
private function checkBehavior(string $ip): void
{
$key = self::WINDOW_KEY . $ip;
$count = Redis::incr($key);
if ($count === 1) {
Redis::expire($key, self::WINDOW_SIZE);
}
if ($count > 500) {
$this->addToBlacklist($ip);
}
}
private function checkSpecialPaths(Request $request, string $ip): void
{
$path = $request->path();
$limits = [
'/api/login' => ['limit' => 5, 'ttl' => 60], // 登录:1分钟5次
'/api/register' => ['limit' => 3, 'ttl' => 300], // 注册:5分钟3次
'/api/sms/send' => ['limit' => 5, 'ttl' => 3600], // 短信:1小时5次
'/api/reset-pwd' => ['limit' => 3, 'ttl' => 1800], // 找回密码:30分钟3次
];
foreach ($limits as $pattern => $rule) {
if (str_starts_with($path, $pattern)) {
try {
Limiter::check($ip . ':' . $pattern, $rule['limit'], $rule['ttl']);
} catch (RateLimitException $e) {
throw new RateLimitException("操作过于频繁,请稍后重试");
}
}
}
}
}
2.3 注册全局中间件
php
// config/middleware.php
return [
'' => [
app\middleware\CcProtect::class,
],
];
2.4 限流组件配置
php
// config/plugin/webman/limiter/app.php
return [
'enable' => true,
'driver' => 'redis', // 生产环境用 redis
'stores' => [
'redis' => [
'connection' => 'default',
]
],
'ip_whitelist' => [
'127.0.0.1',
],
];
三、fail2ban 服务器层(兜底)
当攻击流量非常大时,Nginx 和 webman 都可能被撑满。这时候用 fail2ban 在服务器层通过 iptables 直接拉黑 IP。
3.1 安装
bash
yum install fail2ban -y
3.2 创建规则配置
ini
# /etc/fail2ban/jail.d/cc-attack.conf
[nginx-cc]
enabled = true
filter = nginx-cc
action = iptables-multiport[name=cc, port="http,https"]
logpath = /www/wwwlogs/cc_attack.log
maxretry = 3
findtime = 60
bantime = 600
3.3 创建 filter
ini
# /etc/fail2ban/filter.d/nginx-cc.conf
[Definition]
failregex = ^\[.*\] CC攻击拦截 IP: <HOST>
ignoreregex =
3.4 启动
bash
systemctl enable fail2ban
systemctl start fail2ban
四、参数调优建议
开发 / 测试阶段
| 参数 | 建议值 |
|---|---|
| 全局限流 | 100r/s |
| 突发 burst | 20 |
| 行为阈值 | 500次/60s |
| 黑名单 TTL | 10 分钟 |
| 登录限流 | 5次/60s |
生产环境(按需调整)
先跑几天观察日志,再精细调参:
bash
# 查看 CC 攻击日志
tail -f /www/wwwlogs/cc_attack.log
根据实际流量调整行为阈值,别设太低,否则 VPN 用户、办公室共用出口 IP 会被误封。
五、其他建议
5.1 启用 nginx X-Forwarded-For
确保 nginx 配置正确传递真实 IP:
nginx
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
5.2 CDN 层防护(如果有)
在 Nginx 层之前加一层 CDN(如 CloudFlare),CDN 本身就有 DDoS / CC 防护能力,可以挡掉大部分攻击。
5.3 日志分析
定期分析被拦截的 IP,判断是真实攻击还是误封:
bash
# 统计被拦截次数最多的 IP
awk '{print $NF}' /www/wwwlogs/cc_attack.log | sort | uniq -c | sort -rn | head -20
六、排查清单
- [ ] Nginx 限流配置已添加
- [ ] webman/limiter 已安装
- [ ] CcProtect 中间件已注册为全局中间件
- [ ] 限流驱动设为 redis
- [ ] fail2ban 已配置并启动
- [ ] 日志路径已创建且有写权限
- [ ] 观察日志确认规则生效
评论 (0)
发表评论