如何防止PHP应用被CC攻击?Swoole与Workerman的防护实践
摘要:# PHP应用防CC攻击:Swoole与Workerman实战,说点真话 前两天跟一个做电商的朋友聊天,他一脸苦笑:“网站被CC攻击了,客服电话被打爆,老板脸都绿了。”我问他用的啥防护,他说:“就普通防火墙,配了点Nginx限流。” 我直说了吧——**…
PHP应用防CC攻击:Swoole与Workerman实战,说点真话
前两天跟一个做电商的朋友聊天,他一脸苦笑:“网站被CC攻击了,客服电话被打爆,老板脸都绿了。”我问他用的啥防护,他说:“就普通防火墙,配了点Nginx限流。”
我直说了吧——很多所谓防护方案,PPT很猛,真被打的时候就露馅了。
一、CC攻击到底在打什么?
先别急着上技术方案,咱们得搞清楚对手在干嘛。
CC攻击(Challenge Collapsar)说白了,就是模拟大量正常用户,疯狂请求你的网站。它不搞流量轰炸(那是DDoS),专挑你最耗资源的页面打——比如搜索接口、商品详情页、登录验证。
你可能会想:“我服务器配置还行啊?”但问题往往不是硬件不够,而是并发设计有缺陷。
举个例子:一个PHP页面要查3次数据库、调2次外部API、再生成缓存。平时每秒处理50个请求没问题。但攻击者用1万个IP同时请求这个页面,你的MySQL连接池瞬间爆满,PHP-FPM子进程全部卡死——整个站就瘫了。
这种场景你应该不陌生吧?
二、传统PHP架构的“命门”
咱们常用的“Nginx + PHP-FPM”模式,在CC攻击面前其实挺脆弱的。
致命弱点1:进程模型
每个PHP请求都要起一个FPM进程,进程间资源不共享。攻击者只要并发数超过你的pm.max_children,新用户就连不上了。
致命弱点2:连接复用差
每次请求都要重新连接MySQL、Redis。攻击时,数据库连接数先爆,然后雪崩。
致命弱点3:全局状态难维护
你想做IP限流?得靠Redis。但攻击者可能连你的Redis一起打穿。
我见过不少站点,问题往往不是没上防护,而是配错了——在Nginx层做了个每分钟60次的限流,结果攻击者用500个IP慢慢打,照样把你拖垮。
三、Swoole的防护实践:从“被动挨打”到“主动管控”
Swoole这东西,很多人只知道它性能强,其实它的连接管理能力才是防CC的利器。
1. 连接级限流(这才是关键)
// 在Swoole HTTP Server中
$server = new Swoole\Http\Server('0.0.0.0', 9501);
// 设置连接数限制
$server->set([
'max_connection' => 10000, // 总连接数限制
'max_request' => 1000, // 单个worker最大请求数
]);
// 在onRequest中做IP频率控制
$ip = $request->server['remote_addr'];
$key = 'cc_ip:' . $ip;
// 用Swoole Table做内存级计数(比Redis快得多)
$count = $ipTable->incr($key, 1);
if ($count === 1) {
$ipTable->expire($key, 60); // 60秒窗口
}
if ($count > 100) { // 60秒内超过100次
$response->status(429);
$response->end('请求太频繁');
return;
}
为什么有效?
Swoole Table存在内存里,读写速度是微秒级。攻击者就算换着IP打,你也能在第一个请求处理时就判断出异常。
2. 协程级熔断
这个是我自己项目里用的方案:
use Swoole\Coroutine;
// 对高危接口做并发控制
$semaphore = new Swoole\Coroutine\Channel(10); // 只允许10个并发
Coroutine::create(function () use ($semaphore) {
if ($semaphore->isFull()) {
// 直接返回缓存或默认值,别让请求堆在队列里
return $this->getFallbackData();
}
$semaphore->push(true);
try {
// 处理核心业务
$result = $this->processRequest();
} finally {
$semaphore->pop();
}
return $result;
});
说白了,就是别让所有请求都挤到数据库门口。有些请求该放弃就放弃,保证核心业务不垮。
四、Workerman的独特优势:长连接防CC
Workerman的强项在长连接,但用它防CC攻击,有个很妙的思路。
1. 连接指纹识别
// Workerman的Connection对象有完整信息
$worker->onConnect = function ($connection) {
$ip = $connection->getRemoteIp();
$port = $connection->getRemotePort();
// 生成连接指纹(IP+端口+一些特征)
$fingerprint = md5($ip . $port . $connection->id);
// 记录连接时间
$connection->connect_time = time();
// 如果这个IP短时间内建立太多连接...
$ip_conn_count = $this->getIpConnectionCount($ip);
if ($ip_conn_count > 20) {
// 很可能是CC工具,直接断开
$connection->close();
return;
}
};
2. 基于业务逻辑的验证
Workerman适合做前置验证层:
$worker->onMessage = function ($connection, $data) {
// 第一步:先做轻量级验证
if (!$this->validateToken($data['token'])) {
$connection->send(['code' => 401, 'msg' => '无效令牌']);
return;
}
// 第二步:检查请求频率(用内存缓存)
$client_id = $connection->id;
if ($this->isTooFrequent($client_id)) {
$connection->send(['code' => 429, 'msg' => '慢点请求']);
return;
}
// 第三步:才转发到真正的业务处理
$this->forwardToBusiness($connection, $data);
};
这种分层验证的思路,能把80%的无效请求挡在业务逻辑之外。
五、实战中的“骚操作”(一般人我不告诉他)
1. 动态规则引擎
别把防护规则写死。我有个项目这么干的:
// 监控系统负载,自动调整防护策略
$load = sys_getloadavg()[0];
if ($load > 5.0) { // 负载高了
// 自动收紧限制:从每分钟100次降到30次
$this->config['max_requests_per_minute'] = 30;
// 临时关闭一些非核心功能
$this->disableFeature('search_suggest');
}
2. 人机验证的“软触发”
别一上来就弹验证码,用户体验太差:
- 正常用户:前10次请求直接过
- 可疑IP:第11次请求开始,每5次弹一次简单算术题
- 攻击IP:直接上滑块验证
3. 源站隐藏的“土办法”
如果你的源站还裸奔,你心里其实已经有答案了——迟早要被打。
但高防IP太贵?试试这个方案:
用户 -> Cloudflare(免费版) -> 自建反向代理(境外VPS) -> 你的源站
攻击者打的是Cloudflare的IP,你的真实IP从来没暴露过。成本?就一台境外VPS的钱,一个月不到100块。
六、别踩这些坑(都是血泪教训)
-
别过度依赖Nginx限流
limit_req模块是单机版的,集群环境下会漏。而且它不识别业务逻辑——重要的支付接口和不重要的公告页面,被一视同仁了。 -
别在数据库层做防护
见过有人在MySQL里写触发器统计IP访问次数——攻击还没防住,数据库先挂了。 -
别相信“智能防护”的鬼话
很多云WAF说能自动识别CC攻击,实际上就是基于频率的规则。你得自己根据业务调参数,没有一劳永逸。 -
别忽视业务逻辑漏洞
有个经典案例:某论坛的“站内搜索”没做限制,攻击者用脚本疯狂搜索,把ES集群搜挂了。这种攻击,防火墙根本识别不出来。
七、该选Swoole还是Workerman?
说点实在的:
选Swoole如果:
- 你已经用Swoole重构了项目
- 需要高性能的HTTP API服务
- 愿意投入时间学习协程编程
选Workerman如果:
- 你需要处理大量长连接(WebSocket、物联网)
- 项目里有很多现成的PHP代码,想平滑迁移
- 对协程不是必须,进程模型也能接受
两个都不选如果:
- 你的项目很小,每天就几千PV
- 团队里没人懂这两个框架
- 那你不如直接上云WAF,虽然要花钱,但省心
最后说几句
防CC攻击这事,真不是上个软件就能解决的。你得:
- 了解自己的业务瓶颈在哪里(数据库?缓存?API?)
- 选择合适的防护层级(网络层?应用层?)
- 准备好降级方案(被打的时候,保核心功能)
我见过最惨的一个案例:公司被CC攻击,技术负责人第一反应是“加服务器”。加了10台,攻击者把并发数翻了一倍——还是挂。一天烧掉好几万,问题没解决。
所以啊,防护的本质是成本和攻击成本的博弈。你要做的不是绝对防住(那不可能),而是让攻击者觉得“打你不划算”。
行了,不废话了。如果你的PHP应用还没做CC防护,今天下班前,至少把IP频率限制加上吧。别等真被打的时候,手忙脚乱。
(对了,上面那些代码片段,都是我项目里实际在用的。你抄的时候记得改改参数,别直接复制。)

