高频访问站点一旦被盯上,最直接的表现不是带宽跑满,而是正常用户开始访问变慢、登录接口频繁返回验证码、日志里出现大量相同UA或相同IP段的请求。很多人第一反应是封IP,但封IP在CC攻击面前基本无效,因为攻击源往往是分布式的,封掉一批又来一批,而且容易误伤出口IP相同的企业用户或学校用户。真正有效的防护思路是分层处理:先识别请求特征,再在边缘层做行为校验,最后在应用层做资源保护。
先区分扫描型漏洞探测和CC攻击
扫描型漏洞探测的特征非常明显:请求路径里会出现大量不存在的目录、备份文件后缀、管理后台路径,比如/admin、/backup.zip、/.env、/phpinfo.php这类。这类请求通常来自固定的几个IP或IP段,请求频率不一定高,但路径特征集中。处理这类请求最有效的方式不是在应用层判断,而是在接入层直接做路径白名单。把站点真实存在的路径和参数结构梳理清楚,凡是命中已知漏洞探测特征的请求直接返回444或403,不进入后端应用。Nginx里可以用location加正则匹配,把常见漏洞路径和敏感文件后缀一次性拦截。
location ~* \.(env|sql|bak|zip|tar|gz|log|swp|old)$ {
return 444;
}
location ~* ^/(admin|phpmyadmin|backup|db|config|wp-admin|manager|console) {
return 444;
}
这种拦截不消耗后端资源,也不依赖IP信誉库,对扫描器有立竿见影的效果。但要注意,如果你的站点本身就有/admin这类路径,需要把真实路径排除掉,或者改成仅允许特定IP访问。很多扫描器会先探测再攻击,把探测阶段掐断,后续的漏洞利用请求就不会出现。
CC攻击的核心是消耗资源
CC攻击和扫描不一样,它请求的是真实存在的页面,尤其是那些需要查询数据库或执行复杂计算的接口,比如搜索、列表翻页、登录、验证码发送、支付回调等。攻击者用大量代理IP或肉鸡,以正常请求的形式打过来,单看每个请求都没问题,但合在一起就把后端资源耗尽。防护CC攻击的关键不是识别攻击者,而是提高攻击成本,同时保护后端资源不被单个请求拖垮。
第一层是频率限制。频率限制不能只看IP,因为攻击者会换IP。要结合IP、UA、Cookie、请求路径多个维度做组合限速。比如同一个IP对同一个搜索接口每秒超过3次就触发验证码,同一个Cookie在10秒内请求超过20次就降低优先级。Nginx的limit_req模块可以做到基础限速,但更精细的策略需要配合Lua或网关层实现。
limit_req_zone $binary_remote_addr zone=search_limit:10m rate=3r/s;
location /api/search {
limit_req zone=search_limit burst=5 nodelay;
proxy_pass http://backend;
}
这里rate=3r/s表示每秒允许3个请求,burst=5表示允许突发5个,超过就直接拒绝。但要注意,limit_req是针对单IP的,如果攻击者IP池很大,单IP限速就失效了。这时候需要引入全局维度的限速,比如整个站点对搜索接口的总请求量超过阈值就启动全局降级,让所有搜索请求都走缓存或者直接返回默认结果。
第二层是行为校验
CC攻击的请求往往是脚本生成的,虽然模拟了UA和Cookie,但行为模式和真实用户有差异。比如真实用户访问页面会有停留时间,会加载页面里的静态资源,会有鼠标移动和点击事件,而攻击脚本通常只请求目标URL,不请求静态资源,也不会执行JavaScript。利用这个差异,可以在边缘层做无感校验:给首次访问的客户端下发一段JavaScript,要求它计算一个简单的挑战值并回传,验证通过后才放行到后端。这就是常见的JS挑战或Cookie挑战机制。
这种校验对搜索引擎爬虫和正常浏览器用户基本无感,但对简单的HTTP脚本攻击非常有效,因为脚本通常不解析JavaScript。实现方式可以用Nginx的Lua模块配合Redis做挑战令牌管理,也可以用现成的WAF产品。关键点在于挑战逻辑不能太复杂,否则影响正常用户体验,也不能太简单,否则攻击者用无头浏览器就能绕过。比较实用的做法是:对高频IP或高频Cookie触发挑战,对正常流量不触发。
第三层是后端资源保护
有些接口天然消耗资源,比如搜索、排序、大列表查询。即使没有攻击,正常流量高峰也可能把数据库连接池打满。这类接口必须做缓存和降级。高频访问的站点,搜索接口的返回结果在短时间内变化不大,可以把结果缓存到Redis或内存中,设置3到5秒的过期时间。这样即使攻击者打过来,后端也不会每次都查数据库,而是直接返回缓存内容。缓存击穿的问题可以通过加锁或预加载解决,但在这个场景下,缓存的主要目的是吸收攻击流量,所以过期时间短一点反而更安全。
对于登录接口,CC攻击经常用撞库或暴力破解的方式消耗认证服务。除了限速,还应该引入验证码、账号锁定、设备指纹等机制。验证码不要对所有请求都显示,而是在同一账号失败次数超过阈值或同一IP请求频率异常时触发。设备指纹可以识别出同一台设备换了不同IP来请求的情况,对分布式攻击有很好的识别效果。
边缘层和源站的分工要清晰
很多站点被CC攻击打挂,是因为边缘层没有把攻击流量过滤干净,导致源站直接暴露在攻击之下。正确的做法是:边缘层负责限速、挑战、黑名单、路径拦截,源站只处理通过边缘层校验的请求。如果条件允许,可以在边缘层做请求队列和优先级调度,把通过校验的请求放入队列,按后端处理能力匀速转发,避免瞬时流量冲击源站。
这里有一个容易被忽视的点:边缘层自身的性能。Nginx的limit_req和Lua脚本在高并发下也会消耗CPU和内存,如果边缘层节点配置不够,反而会成为瓶颈。高频访问站点的边缘层应该保持轻量,尽量用C模块或高效的Lua逻辑,避免在请求路径上做复杂的正则匹配或多次Redis查询。可以把IP黑名单、路径白名单这类静态规则编译成内存表,减少每次请求的查找开销。
日志和监控是防护的眼睛
没有日志和监控,防护就是盲人摸象。高频访问站点应该把访问日志结构化,记录IP、UA、Cookie、请求路径、响应状态码、响应时间、上游响应时间等字段。当攻击发生时,通过日志分析快速找出攻击特征,比如某个路径的请求量突然放大、某个UA的占比异常、某个IP段的响应时间变长。这些特征可以反过来用于更新防护规则。
监控方面,要关注边缘层的连接数、请求速率、拒绝请求数、挑战通过率,以及源站的CPU、内存、数据库连接数、慢查询数量。当挑战通过率突然下降时,说明可能有攻击者在尝试绕过校验;当数据库连接数持续高位时,说明后端资源保护可能失效。把这些指标做成告警,可以在攻击初期就发现并处理。
实战中常见的几个误区
第一个误区是过度依赖IP封禁。很多运维看到攻击就封IP,结果封了几百个IP,攻击还在继续,反而把正常用户挡在外面。IP封禁只能作为临时手段,不能作为主要防护策略。第二个误区是把所有请求都加上验证码。验证码虽然能挡住脚本,但严重影响用户体验,而且攻击者可以用打码平台绕过。验证码应该只在风险较高时触发。第三个误区是忽略静态资源的防护。很多人认为CC攻击只打动态接口,其实攻击者也会打静态资源,尤其是大文件下载或图片加载,这些请求虽然不消耗数据库,但会消耗带宽和文件句柄。静态资源应该走CDN缓存,源站只处理回源请求。
防护策略要动态调整
攻击者的手法在变,防护策略也不能一成不变。高频访问站点应该建立一套从检测到响应的闭环流程:日志分析发现异常,提取攻击特征,更新边缘层规则,观察防护效果,再根据新的日志调整策略。这个过程可以是半自动的,比如用脚本分析日志,自动生成Nginx规则并加载,但关键变更需要人工确认,避免误伤。
另外,防护策略要考虑业务特性。电商站点的大促期间,正常流量本身就是平时的几倍甚至几十倍,这时候限速阈值要相应调高,否则会把正常用户挡在外面。内容站点的搜索接口和详情页是重点保护对象,而社交站点的消息发送接口更容易被滥用。不同业务的防护重点不同,不能套用统一的模板。
高频访问站点的漏洞防护和CC攻击拦截,本质上是一场资源消耗的对抗。攻击者用低成本的方式消耗你的资源,你要做的是提高攻击成本,同时降低自身资源的消耗速度。路径白名单、频率限制、行为校验、缓存降级、边缘层隔离,这些手段组合起来,才能形成有效的防护体系。单独使用任何一种手段都有局限性,只有分层配合,才能在攻击面前保持站点的可用性。
