如何防止Node.js应用被CC攻击?中间件限流配置
摘要:# Node.js防CC攻击,别光靠中间件,这几点没做等于白搭 说实话,我见过太多Node.js项目,一上线就被CC攻击打懵了。开发者一脸困惑:“我明明配了限流中间件啊!” 问题就出在这儿——**很多人把“中间件限流”当成了防CC的全部,这跟只穿条裤衩…
Node.js防CC攻击,别光靠中间件,这几点没做等于白搭
说实话,我见过太多Node.js项目,一上线就被CC攻击打懵了。开发者一脸困惑:“我明明配了限流中间件啊!”
问题就出在这儿——很多人把“中间件限流”当成了防CC的全部,这跟只穿条裤衩就上战场没啥区别。
一、CC攻击到底在打你哪里?
先别急着配置中间件,咱们得搞清楚敌人往哪儿捅刀子。
CC攻击(Challenge Collapse)说白了,就是用大量肉鸡模拟正常用户,疯狂请求你那些最耗资源的接口。比如:
- 登录接口(频繁验证密码)
- 搜索接口(数据库全表扫描)
- 文件上传接口(I/O操作)
- 复杂的报表生成接口(CPU密集型)
我去年帮一个电商站做应急响应,他们的促销活动页被CC攻击,攻击者就盯着“商品详情查询”这个接口打。那个接口设计得有点问题——每次查询都要关联5张表,还带缓存穿透风险。
攻击者用几百个代理IP,每秒发起上千次请求,数据库连接池瞬间爆满。你猜怎么着?他们确实用了express-rate-limit中间件,但只限制了总请求数,没按IP区分。
结果就是:正常用户和攻击者一起被限流,网站“半死不活”,活动彻底搞砸。
二、中间件限流,你得这么配才管用
好了,现在咱们说回中间件。市面上常见的express-rate-limit、koa-ratelimit,用起来简单,但默认配置基本等于没防。
1. 别用“一刀切”的全局限流
这是我见过最多的错误配置:
// 这种配置,攻击者笑出声
const rateLimit = require('express-rate-limit');
app.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 每个IP限制100次请求
}));
问题在哪?攻击者根本不用同一个IP。现在代理IP便宜得很,1块钱能买几百个。你这100次/15分钟的限制,人家换个IP就绕过去了。
2. 分层限流才是正解
你得根据接口的“脆弱程度”区别对待:
// 1. 对登录接口要狠一点
const loginLimiter = rateLimit({
windowMs: 60 * 1000, // 1分钟
max: 5, // 1分钟内只能试5次
message: '登录尝试过于频繁,请1分钟后再试',
skipSuccessfulRequests: true // 登录成功的不计数,这个很关键
});
// 2. 对搜索接口中等限制
const searchLimiter = rateLimit({
windowMs: 10 * 1000, // 10秒
max: 20,
keyGenerator: (req) => {
// 结合IP和搜索关键词来限流,防止针对某个关键词的CC
return `${req.ip}:${req.query.keyword || 'empty'}`;
}
});
// 3. 对静态资源宽松些
const staticLimiter = rateLimit({
windowMs: 60 * 1000,
max: 300 // 静态资源可以放宽
});
// 分别应用到不同路由
app.post('/api/login', loginLimiter, loginHandler);
app.get('/api/search', searchLimiter, searchHandler);
app.use('/public', staticLimiter);
看到区别了吗?不同的接口,不同的限流策略。登录接口要严防暴力破解,搜索接口要防数据库被打爆,静态资源可以适当放宽。
3. 别忘了Redis存储
如果你的Node.js应用是多实例部署(比如用PM2起了4个进程),内存存储的限流就失效了。每个进程都有自己的计数器,攻击请求被负载均衡到不同实例,限流形同虚设。
必须用Redis做集中存储:
const RedisStore = require('rate-limit-redis');
const redisClient = require('./redis-client'); // 你自己的Redis连接
const limiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.call(...args),
}),
windowMs: 60 * 1000,
max: 100
});
这样所有实例共享同一个计数器,限流才真正生效。
三、中间件之外,这些防线更重要
如果只靠中间件限流,那就像打仗只修了第一道防线——敌人绕过去你就完了。
1. WAF(Web应用防火墙)必须上
很多云服务商都提供WAF,比如阿里云WAF、腾讯云WAF。它们能做的事,比你的中间件多多了:
- IP信誉库:自动封禁已知的攻击源IP
- 人机验证:对可疑请求弹出验证码(别用那种简单的数字验证,现在AI都能识别了)
- 规则引擎:自定义防护规则,比如“1分钟内同一IP访问超过50个不同商品详情页”就触发防护
我建议至少开启WAF的CC防护规则,很多攻击在到达你Node.js应用之前就被拦截了。
2. CDN不只是加速,更是盾牌
把静态资源甚至API都放到CDN后面,好处太多了:
- 隐藏真实服务器IP(源站隐藏),攻击者找不到你真实地址
- CDN自带防护,像Cloudflare就有免费的CC防护(虽然对高级攻击不够用,但能挡掉大部分脚本小子)
- 边缘计算:可以在CDN边缘节点做初步的限流和过滤
有个坑得提醒你:如果API走CDN,注意敏感接口不要缓存。比如登录接口、支付接口,一定要设置Cache-Control: no-store。
3. 业务层防护:最容易被忽略的
这才是真正体现“业务理解深度”的地方。很多CC攻击是利用你业务逻辑的漏洞。
案例时间:我接触过一个在线教育平台,他们的“视频观看进度同步”接口被CC攻击。攻击者伪造大量用户ID,每秒发送几千次进度更新请求。数据库的写入锁竞争导致正常用户请求超时。
他们的解决方案很有意思:
// 在业务层增加“进度更新最小间隔”限制
const userLastUpdate = new Map();
app.post('/api/video/progress', async (req, res) => {
const { userId, videoId, progress } = req.body;
const key = `${userId}:${videoId}`;
const lastUpdate = userLastUpdate.get(key);
const now = Date.now();
// 5秒内只允许更新一次进度
if (lastUpdate && (now - lastUpdate < 5000)) {
// 不是拒绝请求,而是返回成功但不真正更新数据库
// 避免影响用户体验,同时减轻数据库压力
return res.json({ code: 0, message: 'success' });
}
userLastUpdate.set(key, now);
// 真正的数据库操作
await updateProgressInDB(userId, videoId, progress);
res.json({ code: 0, message: 'success' });
});
这种业务层防护,攻击工具很难绕过,因为它和你的业务逻辑紧密相关。
四、监控和应急:被打的时候才知道谁在裸泳
“防”是一方面,“知道被打”和“快速响应”同样重要。
1. 监控这些指标,异常马上报警
- QPS(每秒查询数)突增:特别是某个特定接口
- 响应时间飙升:原来100ms的接口突然变成2s
- 错误率升高:特别是499(客户端提前关闭连接)、502(网关错误)
- 服务器负载:CPU、内存、网络IO的异常波动
建议用Prometheus + Grafana做个监控面板,阈值设得敏感一些。我自己的经验是:白天QPS比平时高3倍,或者晚上2点到5点(国内低峰期)有明显流量,都值得关注。
2. 应急方案提前准备好
别等被打的时候才临时想方案。提前准备好“一键切换”:
- 备用清洗服务:知道怎么快速接入阿里云高防IP或者腾讯云DDoS防护
- 降级方案:哪些接口可以暂时返回缓存数据,哪些可以暂时关闭
- 扩容脚本:服务器怎么快速扩容(虽然对CC攻击效果有限,但能争取时间)
最重要的是:提前和团队演练一次。真出事的时候,大家不至于手忙脚乱。
五、最后说几句大实话
防CC攻击是个系统工程,中间件限流只是其中一环,而且往往不是最关键的那环。
我看到太多团队把时间全花在调限流参数上,却忽略了WAF配置、CDN设置、业务层防护这些更有效的手段。这就好比家里防盗,你花了重金买最贵的门锁,却忘了关窗户。
Node.js应用因为其单线程、异步IO的特性,其实对CC攻击有一定“天然抗性”——至少不容易像PHP那样一个请求就卡住整个进程。但这也让开发者容易掉以轻心。
记住一个原则:防御要分层,监控要及时,应急要演练。
你的Node.js应用现在防CC措施到哪一层了?如果还只是靠一个中间件,我劝你今晚就加个班,把WAF和CDN先配起来。真等被打的时候,哭都来不及。
行了,不废话了,配置去吧。

