Cloudflare防CC误杀百度蜘蛛,需关闭Bot Fight Mode全局开关,避免自定义规则无效。单独放行User-Agent易被攻击者利用。解决需先关闭Bot Fight Mode,再自定义WAF规则精细控制防CC攻击。
几个月前我手头一个内容站被海外肉鸡盯上了。攻击者专门挑高开销的搜索和标签聚合页面刷请求,Nginx 访问日志里一秒钟刷出几百条动态查询,CPU 使用率直接顶在 95% 上下。
为了先把服务器保住,我顺手在 Cloudflare 后台开启了代理加速,顺便在安全设置里把 Bot Fight Mode(Bot 战斗模式)打开了,安全级别也顺手切成了 High。效果立竿见影,攻击流量几乎瞬间在边缘节点被全数挡下,服务器负载回落到日常的 5% 左右。
我当时以为事情就这么解决了。直到两周后例行检查百度搜索资源平台,抓取频次曲线直接走出了断崖式下跌,从每天两万多次骤降到个位数。点进抓取诊断工具测试,所有页面清一色报错:HTTP 403 或者抓取超时,返回的内容长度只有区区两千字节。
调出 Cloudflare 的安全事件日志一查,才发现这半个月里被挡在门外的根本不只是肉鸡,连带着真真正正的百度蜘蛛(Baiduspider)全被当成恶意脚本拦截了。
为什么 Cloudflare 会把国内爬虫往死里拦?
很多人以为 Cloudflare 识别搜索引擎是一视同仁的。实际情况是,Cloudflare 官方维护的 Verified Bots(已验证爬虫)名单里,核心主要是 Googlebot、Bingbot、Applebot 以及 Yandex 这些海外搜索引擎。
至于百度、搜狗、360 这些国内搜索引擎,根本不在 Cloudflare 的默认白名单数据库里。在 Cloudflare 的边缘节点眼里,Baiduspider 和某个用 Python requests 写的爬虫脚本没有任何区别,都属于未验证的自动化机器人。
一旦你开启了免费版自带的 Bot Fight Mode,Cloudflare 会在边缘计算层对所有非已验证机器人的访问强制下发 JavaScript 质询(JS Challenge,也就是 Turnstile 人机验证)。Googlebot 顶着官方白名单可以大摇大摆穿透过去,百度蜘蛛访问时却只会拿到一个包含混淆 JS 代码的 403 页面。
百度的爬虫调度系统并不像桌面端浏览器那样执行完整的交互脚本。爬虫程序发了个 GET 请求,收到 403 错误码或者一段无法解析正文的防御脚本,重试几次之后调度算法就会自动判定该目标站点服务不可用。接下来的连锁反应是下调抓取配额,标记为死链,最后全站索引库大面积剔除。
坑一:Bot Fight Mode 是全局硬开关,自定义规则根本绕不过去
发现是 Bot 战斗模式惹的祸之后,我第一反应是去 WAF 页面加一条自定义规则,想着把包含 Baiduspider 的请求放行就好了。
我在 WAF Custom Rules 里面写了一条规则:
匹配 http.user_agent contains "Baiduspider",操作选择 Skip(跳过所有安全检查)或者 Allow。
规则上线后我立刻用百度的抓取诊断工具测试,结果毫无变化,抓取依然报错 403。
翻了 Cloudflare 官方文档的执行层级才明白,在 Cloudflare 的免费版(Free Plan)和部分基础 Pro 版里,Bot Fight Mode 属于底层的全局托管规则,它的执行优先级高于用户在控制台自定义配置的 WAF Custom Rules。请求刚到达边缘节点就被 Bot Fight Mode 强行抓去弹验证码了,根本走不到你的 Custom Rules 这一层。
要让国内爬虫正常通行,第一个动作必须是彻底关闭这个全局开关。路径在:
Cloudflare 控制台 -> Security(安全性) -> Bots(机器人) -> 将 Bot Fight Mode 彻底切换为 Off。
只要这个全局开关开着,写再多放行规则都是徒劳。
坑二:单纯放行 User-Agent 等于给攻击者留了直通后门
关掉全局 Bot Fight Mode 之后,网站如果还要防 CC 攻击,就必须靠自定义 WAF 规则来做精细控制。
很多人图省事,直接在 WAF 里配一条规则:
当请求头包含 Baiduspider 时直接放行(Allow)。
这么配上线不到三天,我就吃到了苦头。攻击者扫描发现站点放行了特定 User-Agent,直接在攻击脚本的请求头里伪造了一行:
User-Agent: Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
十几万个代理 IP 顶着百度的爬虫头蜂拥而入,所有流量绕过 Cloudflare 的限速和质询规则,直接把源站 MySQL 打到死锁。
只校验 User-Agent 是最脆弱的防御。伪造 HTTP 请求头没有任何技术门槛,把 UA 放行写成绝对信任,等于自己把防护罩戳了个洞。
坑三:试图用 ASN 编号做全量白名单的盲区
意识到 UA 会被伪造后,我又尝试通过自治系统编号(ASN)来限制。百度蜘蛛的 IP 归属主要在电信、联通以及百度自己的 AS 域内,常见如 AS4134(中国电信)、AS4837(中国联通)或者 AS23724。
但很快我就发现这个方案同样行不通。中国电信 AS4134 下面挂着数以亿计的普通家用宽带和云服务器,如果把 AS4134 整个加入放行白名单,国内发起的僵尸网络攻击就完全失去防护了。
更麻烦的是,百度官方从来不提供一套固定不变的静态 IP 列表,爬虫 IP 会随着抓取节点的动态扩容随时轮换,单靠在 Cloudflare 规则里维护静态 IP 掩码,过不了几天就会因为漏掉新节点而导致抓取失败。
真正可行的防护与放行架构
摸透上面几个坑之后,我把防护逻辑拆成了两层,一层在 Cloudflare 边缘处理,另一层放在源站 Nginx 做兜底反查。
1. Cloudflare WAF 自定义规则分层配置
进入 Cloudflare 控制台 -> Security -> WAF -> Custom rules,创建以下几组规则(按优先级从上到下排列):
规则 A:信任已知国际搜索引擎(优先级最高)
表达式语言(Expression):
(cf.client.bot)
操作选择:Skip,跳过所有 WAF 组件与质询。Googlebot、Bingbot 本身就受 Cloudflare 官方加密签名校验,这部分直接放行没有任何安全风险。
规则 B:针对疑似百度蜘蛛的放行与降级策略
我们要放行百度蜘蛛,但不能无条件放行。我们可以结合威胁分值(cf.threat_score)来做过滤。Cloudflare 会对每个访问 IP 评估威胁分数,正常的百度爬虫节点威胁分通常极低:
(http.user_agent contains "Baiduspider" and cf.threat_score lt 10)
操作选择:Skip(跳过后续的 Challenge 质询)。
对于伪造百度 UA 但威胁分较高的恶意 IP(比如经常参与扫描被标记的肉鸡),这条规则不会放行,请求会自然落入后面的防御规则中。
规则 C:高敏感路径强行设防
爬虫正常抓取的都是内容页、列表页和静态资源,绝不可能去访问你的后台登录、支付接口或者数据提交接口。针对这些路由,坚决不给任何爬虫特权:
(http.request.uri.path contains "/admin" or http.request.uri.path contains "/api/user/")
操作选择:Managed Challenge(托管质询)。
规则 D:全站兜底防护规则
对于剩下的普通访问者,设置合理的频率限制(Rate Limiting)或者对高风险地区开启质询:
(cf.threat_score gt 20 and not cf.client.bot)
操作选择:Managed Challenge。
2. 源站 Nginx 真实 IP 穿透与反向验证
请求经过 Cloudflare 转发后,如果源站 Nginx 没做配置,日志里记录的全部是 Cloudflare 的代理节点 IP。要判断是不是真爬虫,源站必须先还原访客的真实 IP。
在 Nginx 主配置文件的 http 块中加入 Cloudflare 官方 IP 段与真实头部定义:
# 信任 Cloudflare 边缘节点 IP 段
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
# 指定获取客户端真实 IP 的请求头
real_ip_header CF-Connecting-IP;
重载 Nginx 之后,$remote_addr 就会正确显示为来访爬虫的公网 IP。
3. 用 rdns 双向反查彻底封死伪造蜘蛛
百度官方给出的真伪识别标准只有一条:反向 DNS 解析(rdns)。真百度蜘蛛的 IP 做 PTR 反查,主机名必定以 .baidu.com 或者 .baidu.jp 结尾,而且该主机名再做正向 A 记录解析,解析出来的 IP 必须和请求源 IP 完全一致。
在 Linux 终端手动验证某个来访 IP 非常直观:
# 1. 反查 PTR 记录
host 116.179.32.194
# 预期输出:194.32.179.116.in-addr.arpa domain name pointer baiduspider-116-179-32-194.crawl.baidu.com.
# 2. 正向验证 A 记录
host baiduspider-116-179-32-194.crawl.baidu.com
# 预期输出:baiduspider-116-179-32-194.crawl.baidu.com has address 116.179.32.194
如果两步都吻合,就是货真价实的百度蜘蛛。只要第一步不出 .baidu.com,或者第二步 IP 对不上,就是伪造 UA 的攻击肉鸡。
我在服务器后台放了一个小脚本,每隔十分钟扫描一次 Nginx 日志中标记为 Baiduspider 的独立 IP。一旦发现双向解析对不上的伪造 IP,直接调用 ipset 封禁该 IP 24 小时,并在日志中记录报警。
这样一来,Cloudflare 端既放行了正常低风险的百度抓取,源站又卡死了伪造蜘蛛钻空子打垮数据库的可能。
调整后的验收与数据回暖
规则调整完毕后,先做两轮实测。
第一轮,在本地用 curl 命令模拟百度爬虫请求,观察 Cloudflare 返回的响应头:
curl -I -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" \
https://www.yourdomain.com/some-post.html
检查返回的状态码是不是 HTTP/2 200 或者 HTTP/1.1 200 OK,响应头中必须没有 cf-mitigated: challenge 标记,返回的 content-length 也必须是正常的文章页面体积。
第二轮,登录百度搜索资源平台,找到 抓取诊断 工具,手动输入几个不同的内页 URL 发起抓取诊断。如果配置正确,诊断结果会在几十秒内显示为 抓取成功,查看抓取头部状态码为 200,抓取正文也能完整展示页面真实的 HTML 内容。
我的站点在清理掉 Bot Fight Mode 误杀之后的第四天,百度站长平台的抓取频次曲线开始出现明显抬升,一周左右恢复到了正常的抓取水位,停滞的快照和新文章收录也逐渐回到了常规节奏。
给站点上安全防护不能只顾着看攻击拦截率。边缘防护规则多拦住一个合法蜘蛛,背后可能就是几十个页面索引的无声蒸发。把全局粗暴的规则拆解成精细化的分层防护,才能在挡住攻击的同时把搜索引擎的抓取通道安稳留住。
文章标题:网站套了 Cloudflare 防 CC,百度收录却彻底归零?排查 Bot Fight Mode 误杀蜘蛛、WAF 规则放行与假爬虫穿透我踩过的几个坑
文章链接:https://llbbs.cn/jishujaocheng/241.html
本站文章均为原创,未经授权请勿用于任何商业用途
评论一下?