CC攻击的本质不是拼带宽,而是拼资源消耗。攻击者用大量看似正常的请求,专门瞄准那些最消耗服务器性能的URL,比如数据库查询密集的搜索页、动态生成的列表页、需要频繁读写缓存的接口。一个普通的HTTP GET请求可能只需要几毫秒处理,但一个复杂的搜索请求可能需要几百毫秒甚至几秒,攻击者只要用几千个并发持续打这个点,服务器的CPU、内存、数据库连接池就会迅速耗尽,正常用户的请求全部被拖垮。所以防御CC的核心思路不是“挡住所有流量”,而是“用最小的成本识别并丢弃恶意请求,同时把有限资源留给真实用户”。

CC攻击的常见手法与流量特征

CC攻击在应用层发起,通常借助代理IP池、肉鸡网络或者云函数这类廉价算力。攻击者会先分析目标站点,找出响应时间最长、数据库操作最重的接口,然后构造大量带随机参数的请求。有些攻击会模拟正常浏览器,带上完整的User-Agent、Referer甚至Cookie,让简单的UA过滤失效。流量特征上,CC攻击往往表现为某个URL的请求量在短时间内暴涨,但来源IP极其分散,单个IP的请求频率可能并不高,这就让基于单IP频率的限速策略难以奏效。还有一种更隐蔽的慢速CC,每个请求都维持很长时间的连接,慢慢发送请求头或请求体,用极低的带宽消耗占满服务器的并发连接数。

防御层次一:网络层与边缘过滤

第一道防线应该放在网络边缘,把明显异常的流量挡在应用服务器之前。这包括对源IP的地理位置、ASN、历史信誉做初步判断。如果业务只面向国内用户,那么来自境外数据中心的流量可以直接丢弃或做严格挑战。边缘层还可以配置连接速率限制,比如单个IP每秒新建连接数超过阈值就触发熔断。对于伪造的TCP连接,可以在负载均衡层启用SYN Cookie,避免半连接队列被打满。但边缘过滤只能解决明显的洪水型攻击,对于模拟真实浏览器的CC攻击,还需要更精细的识别手段。

防御层次二:应用层行为分析与挑战机制

应用层防御的关键是区分人和机器。最简单的做法是检查请求头完整性,真实浏览器的请求头字段顺序、Accept、Accept-Language、Sec-Fetch系列字段通常比较完整,而脚本构造的请求往往缺失或顺序异常。但攻击者可以伪造这些字段,所以需要结合行为分析。例如统计每个会话的访问路径,真实用户会先访问首页,然后点击链接进入内页,停留时间有长有短;而CC攻击的请求往往是直接命中目标URL,没有前序页面,访问完立刻断开。基于这个特征,可以对直接访问敏感接口且无Referer的请求进行JS挑战或Cookie验证。JS挑战的原理是返回一段需要执行JavaScript的页面,脚本执行后会生成一个动态Cookie,后续请求必须携带这个Cookie才能通过。攻击脚本如果不支持JS执行,就无法获取有效Cookie,从而被拦截。

防御层次三:频率控制与动态限速

限速是CC防御中最常用的手段,但固定阈值的限速容易误伤正常用户,也容易被攻击者绕过。更好的做法是动态限速,根据服务器的实时负载自动调整阈值。当CPU使用率超过70%或数据库连接池使用率超过80%时,自动收紧限速策略,比如将单IP每秒请求数从20降到5,同时缩短统计窗口。限速的维度不能只看IP,还要结合会话、设备指纹、URL路径。比如对登录接口限制每个账号每小时的尝试次数,对搜索接口限制每个会话每分钟的请求数。对于绕过IP限速的分布式攻击,可以引入设备指纹技术,通过浏览器Canvas渲染、WebGL信息、字体列表等生成唯一标识,即使攻击者更换IP,只要设备指纹相同就能被识别并限制。

防御层次四:资源隔离与降级策略

当攻击流量已经穿透前面的防线时,最后一道保险是资源隔离和业务降级。把高消耗的接口与核心业务隔离,比如搜索服务独立部署,即使搜索被CC打挂,主站商品浏览和下单流程不受影响。对于数据库查询密集的接口,可以引入缓存层,把热点查询结果缓存起来,攻击请求命中缓存时对数据库的压力会大幅降低。降级策略则是当系统负载超过安全阈值时,自动关闭非核心功能,比如关闭推荐算法、关闭实时统计、把动态页面静态化。一个典型的降级配置是:当CPU持续30秒超过85%时,搜索接口返回静态的默认结果列表,而不是实时查询数据库;当内存使用率超过90%时,关闭用户行为日志写入。这些措施不能阻止攻击,但能保证核心业务在攻击期间仍然可用。

实战中的Nginx配置示例

下面给出一段Nginx的CC防御配置,结合了连接限制、请求频率限制和JS挑战。这段配置可以直接用于生产环境,但需要根据实际业务调整阈值。

# 定义限速区域,按IP统计请求频率
limit_req_zone $binary_remote_addr zone=cc_req:20m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=cc_conn:20m;

server {
    listen 80;
    server_name example.com;

    # 单个IP最大并发连接数
    limit_conn cc_conn 20;

    # 对高消耗接口单独限速
    location /search {
        limit_req zone=cc_req burst=20 nodelay;
        proxy_pass http://backend_search;
    }

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

    # 对无Cookie且无Referer的请求进行JS挑战
    location / {
        if ($http_cookie !~* "cc_challenge=passed") {
            if ($http_referer = "") {
                return 302 /challenge.html?redirect=$request_uri;
            }
        }
        proxy_pass http://backend_web;
    }

    # JS挑战页面
    location = /challenge.html {
        root /var/www/challenge;
    }
}

这段配置中,limit_req_zone定义了每个IP每秒最多10个请求,对于/search接口允许突发20个请求,超过后立即丢弃。limit_conn限制单个IP的并发连接数为20,防止慢速CC占满连接。JS挑战部分检查请求是否携带特定Cookie,如果没有且Referer为空,则重定向到挑战页面。挑战页面中的JavaScript会设置Cookie并重新跳转回原URL,真实浏览器能自动完成这个过程,而大多数攻击脚本无法处理。

基于Redis的分布式限速方案

当业务部署在多台服务器上时,单机限速无法应对分布式CC攻击,因为攻击流量会分散到不同节点,每台机器看到的单IP频率都不高。这时需要集中式限速,用Redis做计数器。下面是一个Python实现的滑动窗口限速逻辑。

import redis
import time

r = redis.Redis(host='localhost', port=6379, db=0)

def is_rate_limited(ip, limit=10, window=1):
    key = f"rate:{ip}"
    now = time.time()
    pipe = r.pipeline()
    pipe.zremrangebyscore(key, 0, now - window)
    pipe.zadd(key, {now: now})
    pipe.zcard(key)
    pipe.expire(key, window)
    results = pipe.execute()
    count = results[2]
    return count > limit

这个函数用Redis的有序集合实现滑动窗口,每个请求的时间戳作为score存入集合,先删除窗口外的旧记录,再统计当前窗口内的请求数。如果超过限制就返回True,表示该请求应该被拒绝。多台应用服务器共享同一个Redis实例,就能实现全局统一的限速。对于大规模场景,可以用Redis Cluster或者专门的限速服务来提升吞吐。

设备指纹与无感验证的落地

设备指纹是应对分布式CC攻击的有力工具。它的原理是收集浏览器端的多种特征,包括User-Agent、屏幕分辨率、时区、字体列表、Canvas渲染结果、WebGL渲染器信息等,将这些特征组合成一个哈希值。真实用户的设备指纹相对稳定,而攻击脚本即使修改了User-Agent,也很难完全模拟所有特征。在实际部署中,可以在用户第一次访问时下发一段JavaScript,采集设备信息并生成指纹,将指纹与IP、会话绑定。后续请求如果携带的指纹与绑定记录不一致,就触发验证或直接拒绝。无感验证则是更进一步的手段,它不需要用户做任何操作,而是在后台自动分析鼠标轨迹、键盘节奏、页面停留时间等行为数据,判断是否为真人操作。这些方案的实施成本较高,但对于高价值目标站点来说,是抵御高级CC攻击的必要投入。

监控与应急响应流程

CC防御不是配置完就一劳永逸,攻击手法会不断变化,需要持续监控和调整。监控指标至少包括:每秒请求量、各URL的响应时间、CPU和内存使用率、数据库连接数、限速触发次数、JS挑战通过率。当发现某个URL的请求量异常增长时,要能快速定位攻击特征,比如来源IP段、请求参数规律、User-Agent分布。应急响应流程应该预先制定,包括:第一步确认攻击目标和影响范围,第二步启用预设的限速和挑战策略,第三步分析攻击流量特征并添加针对性过滤规则,第四步如果攻击规模超出防御能力,联系上游运营商或云服务商协助清洗。整个响应过程要记录在案,事后复盘并更新防御策略。

常见误区与注意事项

很多团队在防御CC时容易陷入几个误区。一是过度依赖IP封禁,攻击者使用代理池时封IP基本无效,反而可能误伤正常用户。二是在应用层做太重的防御逻辑,比如每个请求都做复杂的设备指纹计算,这本身就会消耗大量服务器资源,等于帮攻击者放大攻击效果。三是忽略了移动端和API客户端的差异,移动APP的请求头特征与浏览器不同,不能套用同一套JS挑战逻辑,需要为API设计独立的认证和限速机制。四是防御策略过于激进,导致搜索引擎爬虫或正常用户被拦截,影响业务。正确的做法是分层防御、动态调整、保留白名单机制,并且定期用模拟攻击测试防御效果。

CC攻击防御的落地实施本质上是一个持续对抗的过程,没有一劳永逸的方案。从网络边缘的粗粒度过滤,到应用层的行为分析和挑战机制,再到资源隔离与降级,每一层都有其不可替代的作用。真正有效的防御体系需要把这些手段有机结合起来,根据业务特点和攻击态势动态调整策略,同时保持对正常用户体验的最小影响。对于大多数站点来说,先把Nginx限速和JS挑战配置好,再配合Redis集中式限速,就能抵御大部分中低强度的CC攻击。当业务规模增长到一定程度,再逐步引入设备指纹、无感验证和专业的清洗服务,形成完整的纵深防御体系。