CC攻击本质上不是靠带宽“硬打”,而是通过大量看似正常的请求耗尽服务器资源,其中爬虫发起的恶意请求占了很大比例。这类请求往往伪装成搜索引擎蜘蛛、采集工具或监控脚本,单次请求的消耗并不大,但当并发量达到一定阈值后,数据库连接、PHP进程、内存和CPU会迅速被拖垮。所以防御的核心思路不是“识别每一个坏人”,而是“用最低的成本拒绝掉不值得服务的请求”,同时保证正常用户和正规爬虫不受影响。

先要分清一个概念:CC攻击里的“爬虫恶意请求”和正常搜索引擎爬虫是两回事。正常爬虫会遵守robots协议、请求频率有限、会解析页面资源,而恶意爬虫通常不执行JavaScript、不加载CSS和图片、请求路径高度集中、User-Agent可以随意伪造。防御策略要围绕这些行为差异来设计,而不是简单地封IP或者加验证码。

第一层防御:在网络边缘用特征过滤掉明显异常的请求

最有效率的拦截发生在请求到达应用服务器之前。Nginx或OpenResty层面可以做很多事情。首先要检查User-Agent,虽然它可以伪造,但大量恶意爬虫使用的UA库是固定的,例如某些采集框架默认的UA字符串带有“python-requests”“Go-http-client”“Java/1.8”等特征。把这些UA直接返回444或403,成本极低。

其次是请求路径和参数特征。恶意爬虫往往集中抓取列表页、搜索接口、详情页,而且会携带大量分页参数或者随机字符串。可以针对特定URL模式设置频率限制,例如同一个IP在10秒内请求同一个列表接口超过20次就直接拒绝。Nginx的limit_req模块可以完成这个工作,配置示例如下:

http {
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    
    server {
        location /api/list {
            limit_req zone=api_limit burst=20 nodelay;
            proxy_pass http://backend;
        }
    }
}

这个配置的意思是:每个IP访问/api/list接口的速率限制为每秒10次,允许突发20次,超过后立即返回503。对于正常用户来说,手动点击不可能达到这个频率,而恶意爬虫的并发请求会立刻触发限制。

第二层防御:识别“非人类”行为特征并动态挑战

很多恶意爬虫不会执行JavaScript,这是它们和真实浏览器最大的区别。可以利用这一点设置JavaScript挑战。当请求到达时,先返回一个包含简单JS代码的页面,要求客户端计算一个值并重新请求,计算过程会设置一个Cookie。真实浏览器会自动完成这个过程,用户几乎无感知,而纯HTTP客户端无法通过。

在Nginx层面可以通过Lua脚本实现轻量级的JS挑战,不一定要引入重型WAF。核心逻辑是:检查请求是否带有合法Cookie,如果没有,返回一个HTML页面,里面包含一段设置Cookie的JavaScript,然后自动刷新页面。代码思路如下:

location / {
    access_by_lua_block {
        local cookie = ngx.var.cookie_antibot
        if not cookie or cookie ~= "valid_token" then
            ngx.header.content_type = "text/html"
            ngx.say([[]])
            ngx.exit(ngx.HTTP_OK)
        end
    }
    proxy_pass http://backend;
}

这种方式对搜索引擎正规爬虫需要做白名单处理,否则会影响收录。可以通过反向DNS解析验证爬虫来源IP是否属于官方网段,或者直接放行已知的UA和IP段。不过要注意,验证DNS会消耗时间,建议在边缘层做缓存。

第三层防御:基于行为分析的动态限速和IP信誉

固定阈值限速存在误伤风险,因为同一个公司或学校出口IP可能有很多正常用户。更精细的做法是结合行为特征动态调整限速阈值。例如统计每个IP的请求序列中,HTML页面请求和静态资源请求的比例。真实浏览器访问一个页面后,通常会继续请求CSS、JS、图片等静态文件,而恶意爬虫往往只抓HTML内容,静态资源请求比例极低。

可以在Nginx日志中记录每个请求的资源类型,然后通过脚本或实时分析工具计算“静态资源比”。如果某个IP在短时间内请求了大量HTML页面但几乎没有图片和CSS请求,就可以判定为高风险,自动将该IP加入临时黑名单或者提高其限速等级。这种策略比单纯按请求数限速准确得多。

IP信誉库也很实用。很多恶意爬虫使用云服务器IP段,这些IP段通常不属于家庭宽带或移动网络。可以维护一个已知的IDC IP段列表,对来自这些IP的请求默认提高警惕。不过这个列表需要定期更新,而且部分正规企业也可能使用云服务器做代理访问,所以不能一刀切封禁,而是配合其他特征综合判断。

第四层防御:业务层面的数据保护和资源降级

有些恶意爬虫的目标非常明确,就是抓取商品价格、文章内容、用户数据等。即使前面的防御层没有完全拦截,也可以在业务层面做数据保护。例如对价格接口、详情接口做数据混淆或延迟返回。当检测到某个IP的请求频率异常时,不直接拒绝,而是返回缓存中的旧数据或者随机延迟响应,让爬虫获取到的数据失去时效性,同时消耗爬虫自己的资源。

更进一步的策略是“资源降级”。当服务器负载超过阈值时,自动关闭一些非核心功能,例如推荐系统、评论加载、个性化内容,只保留核心页面渲染。这样即使攻击流量打进来,服务器也能维持基本服务,不会完全宕机。这个策略需要在应用代码中实现,通过监控系统实时调整降级开关。

第五层防御:利用缓存和CDN分担压力

对于静态资源和变化不频繁的页面,尽量使用CDN缓存。CC攻击的请求如果命中CDN缓存,就不会回源到服务器,攻击流量被分散到CDN节点上。CDN服务商通常自带基础的CC防护能力,包括IP限速、UA过滤、JS挑战等。但要注意,CDN只能防御针对静态资源的攻击,对于动态接口和个性化内容,请求还是会回源,所以源站的防护不能省略。

在源站层面,可以使用Redis或Memcached缓存热点数据,减少数据库查询次数。很多CC攻击的杀伤力在于让数据库连接数耗尽,如果大部分请求都能从缓存中返回,数据库的压力会大大降低。例如商品详情页的数据可以缓存30秒,即使有大量请求,也只是命中缓存,不会打到数据库。

第六层防御:日志分析和事后追溯

防御不是一次性的,攻击者会不断调整策略。需要持续分析访问日志,找出新的攻击特征。建议将Nginx日志接入实时分析工具,监控关键指标:请求总量、错误率、平均响应时间、独立IP数、UA分布、URL分布等。当某个指标出现异常波动时,及时触发告警。

日志中还可以提取出攻击者的IP、UA、请求路径等信息,用于更新黑名单和限速规则。例如发现某个IP段在短时间内大量请求同一个URL,可以提取该IP段加入防火墙规则。事后追溯还能帮助发现业务漏洞,比如某个接口没有做鉴权,被爬虫直接调用,这就需要从代码层面修复。

实际部署中的注意事项

防御策略要分层部署,不要把所有规则都堆在应用层。边缘层做粗粒度过滤,应用层做细粒度控制,业务层做数据保护。每一层都要有独立的监控和日志,方便定位问题。

白名单机制非常重要。搜索引擎爬虫、合作伙伴的API调用、监控服务等都需要正常访问。建议维护一个白名单列表,包括IP段和UA特征,定期更新。对于白名单内的请求,跳过大部分防御逻辑,但保留基础的频率限制,防止白名单被滥用。

验证码是最后的手段,不要轻易使用。验证码会严重影响用户体验,而且现在的OCR技术可以破解简单验证码,效果有限。只有在确认某个IP或会话是恶意的情况下,才对它启用验证码,而不是对所有用户启用。

防御CC攻击中的爬虫恶意请求,核心是“用最低成本识别并拒绝非必要请求”。通过边缘特征过滤、JavaScript挑战、行为分析、业务降级、缓存和日志分析这六层策略,可以构建一个相对完整的防御体系。实际部署时,要根据自身业务特点调整各层的权重,持续观察和优化规则,才能在保证正常服务的前提下有效抵御攻击。