CC防护(Challenge Collapsar)的核心难点从来不是"防不防得住",而是"防得住的同时别把正常用户也拦了"。很多团队一上来就对全站统一设置每秒10次请求限制,结果登录接口、查询接口、支付接口全部一视同仁,要么正常用户频繁操作被误判,要么真正的攻击流量混在正常请求里根本没拦住。真正有效的做法是按业务接口差异化配置访问频率,把每个接口当成独立的防护单元来对待,根据它的业务特征、用户行为模式、敏感程度分别设定阈值和策略。这篇文章就把这套实践从头到尾讲透,包括怎么分类、怎么定阈值、怎么落地、怎么调优。
一、为什么不能用统一频率阈值做CC防护
统一阈值最大的问题是"一刀切"。举个真实场景:你的网站有三个接口,一个是用户登录(/api/login),一个是商品列表查询(/api/product/list),一个是订单提交(/api/order/submit)。登录接口正常用户一天可能就调几次,但如果有人暴力撞库,一秒能打几百次;商品列表接口用户正常浏览可能每秒刷新两三次,但爬虫可能一秒拉几十次;订单提交接口正常用户几分钟才点一次,但刷单脚本可能一秒提交几十单。如果你统一设成每秒10次,登录接口太松、订单接口太紧,根本不合理。
更关键的是,不同接口被攻击后的后果完全不同。登录接口被打,影响的是用户认证体验;商品接口被打,影响的是页面加载和SEO收录;订单接口被打,直接造成资损。所以防护策略必须跟业务后果挂钩,而不是简单地数请求次数。
二、业务接口分类:差异化配置的前提
在配置频率之前,第一步是把所有接口按业务属性分清楚。我通常建议分成四类:
第一类是高频低敏接口,比如首页静态资源、公共配置接口、健康检查接口。这类接口本身就会被频繁访问,阈值要设得相对宽松,但要区分正常高频和异常高频。
第二类是中频中敏接口,比如商品列表、搜索、用户信息查询。这类接口是CC攻击的重灾区,因为返回数据量大、服务器消耗高,需要设置中等阈值并配合缓存策略。
第三类是低频高敏接口,比如登录、注册、支付、修改密码。正常调用频率很低,一旦出现高频几乎可以确定是攻击,阈值要设得非常严格。
第四类是动态接口,比如短信验证码发送、邮件发送、文件上传。这类接口有明确的业务频率上限(比如一个手机号一分钟只能发一条验证码),需要结合业务规则做硬性限制。
三、差异化阈值设定的具体方法
阈值不是拍脑袋定的,要有数据支撑。我的做法是分三步走:
第一步,采集基线数据。在正常业务时段,对每个接口统计单位时间内的请求量分布。比如统计过去7天登录接口每分钟的请求次数,取P95值(95%的请求都低于这个数)作为正常上限的参考。注意要排除爬虫和监控系统的流量,只统计真实用户行为。
第二步,设定阶梯阈值。不要只设一个固定值,而是设多档。比如登录接口可以设:每分钟超过5次触发验证码,每分钟超过20次直接拦截;商品列表接口设:每秒超过5次触发限流,每秒超过30次直接拦截。阶梯式处理既能挡住攻击,又给误报留了缓冲空间。
第三步,引入滑动窗口算法。固定时间窗口(比如每分钟计数)容易被绕过,攻击者可以在窗口边界打时间差。用滑动窗口更准确,比如维护一个60秒的滑动窗口,每秒滑动一次,统计窗口内总请求数。实现上可以用Redis的Sorted Set或者令牌桶算法。
// 基于Redis的滑动窗口限流示例(伪代码)
function checkRateLimit(apiPath, userId, maxRequests, windowSeconds) {
const key = `rate_limit:${apiPath}:${userId}`;
const now = Date.now();
const windowStart = now - windowSeconds * 1000;
// 移除窗口外的记录
redis.zremrangebyscore(key, 0, windowStart);
// 统计当前窗口内请求数
const currentCount = redis.zcard(key);
if (currentCount >= maxRequests) {
return { allowed: false, reason: 'rate_limit_exceeded' };
}
// 记录本次请求
redis.zadd(key, now, now + Math.random());
redis.expire(key, windowSeconds + 10);
return { allowed: true };
}
四、针对不同接口类型的具体配置策略
下面我按接口类型给出具体的配置建议,这些都是实际项目中验证过的数值范围:
对于登录/注册接口:建议每分钟不超过5次(同一IP或同一账号),触发后先弹验证码,连续触发3次直接封禁15分钟。如果是手机号验证码接口,每个手机号每分钟1次、每小时5次、每天10次,超出直接拒绝。这类接口一定要绑定身份标识,不能只看IP。
对于商品列表/搜索接口:建议每秒不超过3-5次(同一IP),超过后返回降级数据(比如缓存的旧数据或空列表),而不是直接拒绝。因为这类接口被打时,直接拒绝会导致用户看到空白页,体验极差。同时配合CDN缓存和接口级缓存,把压力挡在应用层之前。
对于订单/支付接口:建议每分钟不超过2次(同一用户),且必须结合风控系统判断。如果同一个用户短时间内频繁提交不同金额的订单,即使没超频率阈值也要拦截。这类接口的防护不能只靠频率,要叠加行为分析。
对于文件上传/图片上传接口:建议每分钟不超过1次,且限制单次上传大小。这类接口不仅消耗带宽,还可能被用来上传恶意文件,频率限制只是第一道防线。
五、技术落地架构:怎么把差异化策略跑起来
差异化频率配置要落地,通常有三种架构方式:
第一种是在网关层统一实现。用Nginx、OpenResty或者API网关(如Kong、APISIX)做统一的限流,通过路由规则匹配不同接口路径,应用不同的限流策略。优点是集中管理、性能高,缺点是策略粒度可能不够细。
第二种是在应用层中间件实现。每个接口的Controller前面挂一个限流中间件,中间件根据接口注解或配置读取对应的阈值。优点是灵活,可以结合业务上下文做判断;缺点是每个请求都要经过应用层,性能有损耗。
第三种是混合方案,也是我最推荐的。网关层做粗粒度的IP级限流和全局防护,应用层做细粒度的用户级和接口级限流。两层配合,既不漏防也不误杀。
// 应用层限流中间件示例(Node.js/Express风格)
const rateLimitConfig = {
'/api/login': { max: 5, window: 60000, action: 'captcha' },
'/api/product/list': { max: 5, window: 1000, action: 'degrade' },
'/api/order/submit': { max: 2, window: 60000, action: 'block' },
'/api/sms/send': { max: 1, window: 60000, action: 'block' },
};
function rateLimitMiddleware(req, res, next) {
const config = rateLimitConfig[req.path] || { max: 10, window: 1000, action: 'block' };
const result = checkRateLimit(req.path, req.userId || req.ip, config.max, config.window);
if (!result.allowed) {
if (config.action === 'captcha') {
return res.status(429).json({ code: 429, msg: '请完成验证' });
} else if (config.action === 'degrade') {
return res.json({ code: 200, data: [], from: 'cache' });
} else {
return res.status(429).json({ code: 429, msg: '请求过于频繁' });
}
}
next();
}
六、动态调优:配置不是一劳永逸的事
很多团队配完阈值就不管了,这是大忌。业务会变化、攻击手法会进化,阈值必须持续调优。我建议建立一套监控和反馈机制:
首先,对每个接口的限流触发情况做实时监控。看哪些接口频繁触发限流、触发后用户的后续行为是什么。如果某个接口每天都有大量正常用户触发限流,说明阈值设低了,要调高;如果某个接口从来没触发过但最近被攻击了,说明阈值设高了或者规则有漏洞。
其次,定期做攻击复盘。每次CC攻击事件后,分析攻击流量的特征:打的是哪些接口、频率分布是什么、有没有绕过现有策略。根据复盘结果调整规则,比如发现攻击者在用多个IP轮换打登录接口,那就要把限粒从IP维度扩展到设备指纹或账号维度。
最后,做A/B测试。对于拿不准的阈值,可以灰度发布,比如对10%的流量用新阈值,观察误杀率和拦截率,再全量推广。
七、容易踩的坑和注意事项
第一个坑是只看IP不看用户身份。很多CC攻击用的是肉鸡IP池,单IP频率不高但总量很大。如果只按IP限流,根本防不住。一定要结合用户ID、设备指纹、Session等多维度做识别。
第二个坑是忽略了合法的高频场景。比如大促期间商品接口的正常流量可能是平时的10倍,如果阈值没动态调整,正常用户全被拦了。解决办法是设置活动期间的临时策略,或者接入业务系统的活动信号自动切换阈值。
第三个坑是把限流和封禁搞混。限流是临时控制,封禁是长期惩罚。对于低频高敏接口,超过阈值应该直接封禁而不是限流,因为正常用户根本不会触发。对于中频接口,限流+验证码更合适,给用户一个自救的机会。
第四个坑是没有做好降级预案。限流触发后返回什么很重要。直接返回503会让用户以为系统挂了,返回降级数据或友好提示能大幅降低投诉率。特别是面向C端的产品,用户体验是底线。
八、总结
CC防护中按业务接口差异化配置访问频率,本质上是把"一刀切"变成"精准打击"。核心步骤就四步:先分类、再定基线、设阶梯阈值、持续调优。技术上推荐网关+应用双层架构,策略上要兼顾拦截率和误杀率,运营上要建立监控反馈闭环。没有完美的阈值,只有不断迭代的策略。把每个接口当成独立的防护对象来对待,才能在安全和体验之间找到真正的平衡点。
