高频CC攻击的本质不是“打带宽”,而是“耗资源”。攻击者用大量看似正常的请求,精准命中网站中数据库查询最重、动态生成最频繁的URL,比如搜索接口、登录提交、购物车结算、API数据调用等。这类请求单个看毫无异常,但每秒数千到数万次叠加后,后端应用层和数据库连接池迅速耗尽,服务器CPU飙到100%,正常用户打不开页面。真正要解决高频CC,不能只靠封IP,因为攻击源往往是遍布各地的代理节点,封一个换一个,必须从请求特征识别、资源隔离、动态验证、行为建模四个层面同时下手。

第一步:先定位攻击命中的具体URL和参数特征

高频CC一定有一个或多个集中的攻击目标。登录接口、短信发送接口、搜索框、带复杂SQL查询的列表页是重灾区。你需要在Nginx或Apache日志里按请求频率排序,找出单位时间内访问量异常增长的URL。用下面这条命令可以快速统计某个时间段内请求次数最高的路径:

awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -20

如果某个URL的请求量是正常水平的几十倍甚至上百倍,基本可以确定攻击目标。接下来要进一步看请求参数。很多CC攻击会带上随机生成的查询字符串,目的是绕过CDN或页面缓存。比如搜索接口收到大量无意义关键词,登录接口收到大量不存在的用户名。记录这些参数的格式,后续写规则时可以直接匹配。

第二步:在Web服务器层做第一轮请求过滤

不要等请求打到应用层再处理。Nginx和Apache都有非常高效的过滤能力。针对已经定位的攻击URL,可以在Nginx配置里直接对特定路径做访问频率限制。下面这段配置针对登录接口和搜索接口,限制单IP每秒最多2次请求,超过就返回444(Nginx自定义断开连接状态码,比403更省资源):

http {
    limit_req_zone $binary_remote_addr zone=login:10m rate=2r/s;
    limit_req_zone $binary_remote_addr zone=search:10m rate=5r/s;

    server {
        location /api/login {
            limit_req zone=login burst=5 nodelay;
            proxy_pass http://backend;
        }

        location /api/search {
            limit_req zone=search burst=10 nodelay;
            proxy_pass http://backend;
        }
    }
}

这个方法的优点是性能极高,Nginx处理limit_req几乎不消耗额外CPU。但缺点是它基于IP维度,如果攻击者使用大量代理IP,单IP频率不高,这个规则就拦不住。所以这只是第一层,不能作为唯一手段。

第三步:对动态接口启用强制缓存和静态化

很多高频CC打的是动态请求,但攻击者并不关心返回内容是否实时。比如搜索接口,攻击者发起的请求参数是随机字符串,后端每次都去数据库查一遍,这就是资源消耗的核心。你可以在应用层加一层短时间缓存,把相同参数的查询结果缓存5到10秒。这样即使攻击请求打进来,后端也只在缓存未命中时查一次数据库,后续相同请求直接从内存返回。Redis或Memcached都能做这件事。对于列表页、详情页这类内容变化不频繁的页面,直接生成静态HTML或使用页面缓存,把动态请求转成静态文件访问,CC攻击的杀伤力会大幅下降。

第四步:引入浏览器行为验证,区分真人流量和脚本流量

高频CC攻击工具大多是用Python、Go、Node.js写的脚本,它们不会执行JavaScript,也不会解析复杂的浏览器环境。在关键接口前加一层JavaScript挑战,能有效过滤掉大部分脚本型攻击。具体做法是:当某个IP或某个会话在短时间内请求次数超过阈值,先返回一个带有JavaScript代码的页面,要求客户端计算一个简单的数学题或执行一次重定向,计算正确后才放行到真实接口。真人浏览器会自动完成这个过程,用户几乎无感知,脚本默认不执行JS就会被拦下。

这个方案的关键在于挑战逻辑要轻量,不能影响正常用户体验。可以在Cookie里写入一个加密token,验证通过后后续请求带上这个token,服务端校验通过后放行。对于不执行JS的请求,直接返回空内容或444。

第五步:建立基于行为特征的动态评分机制

单看单个请求,攻击流量和正常流量几乎没有区别。但如果把时间窗口拉长,观察一个IP或一个会话的行为序列,差异就很明显。正常用户访问网站有浏览路径:先打开首页,再点进列表,再查看详情,中间会有停留时间。攻击脚本通常是直接命中目标URL,请求间隔极短且固定,没有Referer或Referer固定,User-Agent批量相同。你可以在应用层或网关层做一个轻量级评分系统:每个IP或会话初始0分,出现以下行为就加分:

1. 请求间隔小于200毫秒且连续超过10次,加3分;

2. 直接访问敏感接口且无Referer,加2分;

3. 同一IP在1分钟内请求超过50个不同参数值的同一接口,加4分;

4. User-Agent与常见浏览器特征不匹配,加2分;

5. 请求参数包含随机字符串或明显无意义内容,加2分。

当总分超过阈值(比如8分),就对该IP或会话实施临时封禁或弹出验证码。这个机制比单纯限频更精准,因为它是多维度的行为判断,不是单一指标。实现上可以用Redis存储评分数据,设置过期时间自动清零,性能开销很小。

第六步:在应用层对数据库查询做严格限制和优化

高频CC的最终目标是打垮数据库。即使前面几层过滤漏掉一部分攻击请求,只要数据库扛得住,攻击效果就会大打折扣。你需要检查所有动态接口的SQL查询,确保每个查询都走索引,没有全表扫描。对于搜索接口,限制关键词长度,比如最长20个字符,超过直接返回空结果。对于列表页,强制分页参数上限,比如page参数最大100,超过就返回空。对于登录接口,加验证码或限制单账号尝试次数,防止攻击者用大量不存在账号打数据库。

数据库连接池也要设置合理上限,不要让单个接口占满所有连接。比如Tomcat的JDBC连接池设置maxActive为100,当连接池满时新请求快速失败返回503,而不是排队等待拖垮整个服务。快速失败比慢速拖死更容易恢复。

第七步:利用边缘节点做分布式清洗

如果攻击流量已经超过单台服务器的处理能力,就必须把清洗能力前移到边缘。现在主流云服务商都提供抗DDoS和CC防护产品,它们在全国甚至全球有大量边缘节点,可以在流量进入源站之前做第一轮清洗。你需要做的是把域名解析到防护IP,并在防护控制台配置针对业务接口的频率限制和行为规则。边缘节点会拦截大部分攻击流量,只把清洗后的正常流量回源。

这里有一个容易被忽视的点:边缘防护的规则要和你源站的规则联动。比如源站发现某个攻击特征,要能通过API实时同步到边缘防护,让边缘节点也拦截相同特征。否则攻击者换个IP就能绕过边缘限制直接打源站。

第八步:准备应急降级方案

高频CC攻击有时会混合其他攻击手段,比如同时发起慢速连接攻击或HTTP Flood变种。任何防护都可能被绕过,所以必须准备降级方案。当检测到后端响应时间超过3秒或错误率超过30%,自动触发降级:关闭搜索建议、关闭个性化推荐、关闭非核心接口,只保留核心页面访问。降级后攻击者即使继续打,能消耗的资源也大幅减少。降级方案要提前写好配置,一键切换,不能等出问题再临时改代码。

另外,日志监控不能断。实时监控Nginx的请求量、后端响应时间、数据库连接数、CPU负载。当这些指标同时异常升高,基本可以确认攻击正在进行。监控告警阈值要设置合理,避免误报,但也不能太宽松,否则发现时服务已经挂了。

高频CC防护的核心思路总结

高频CC攻击防护没有单一银弹,必须多层配合。Web服务器层做基础限频,应用层做行为评分和缓存,数据库层做查询优化和连接限制,边缘节点做分布式清洗,最后用降级方案兜底。每一层都能拦掉一部分攻击,多层叠加后,真正能打到数据库的攻击请求会降到极低。关键是要先定位攻击目标,再针对目标做精准规则,而不是泛泛地全站限频。全站限频会误伤正常用户,精准防护才能既有效又不影响业务。

实际部署时,建议先用日志分析找出攻击URL,然后依次上线Nginx限频、Redis缓存、JS挑战、行为评分。每上线一层,观察攻击流量下降比例和正常用户受影响程度,逐步调优阈值。防护规则不是一次配好就永远有效,攻击者会不断调整手法,你需要根据新的日志特征持续迭代规则。高频CC防护本质上是持续的对抗过程,而不是一次性配置。