Cloudflare 免费版遭遇爬虫洪流:是什么阻止了它,以及我们关闭了哪些功能
运维
一天早上,我们的一个商品详情页网站打不开了。Cloudflare 向访客显示 524 错误,源站每页加载需要两到六分钟,而大约十个小时内没有任何警报。原因是爬虫通过住宅代理以每秒约 225 次请求的速度进行抓取。
洪流的形态
- 数千个地址,每个地址一到三次请求。来自美国、西班牙、法国、巴西、英国等地的家庭宽带连接,正是住宅代理网络出租的那类流量。
- Mac 上的 Chrome 用户代理,版本号从 103 到 151。
- 几乎从不携带 Cookie 或 Referer。在服务器端采样的 2,697 次请求中,只有 4 次携带 Cookie,4 次携带 Referer。
- 两类客户端。约一半未发送
Sec-Fetch-*标头和客户端提示:这是带有复制用户代理字符串的脚本。另一半发送完整的浏览器标头:这是无头浏览器。
每个请求都渲染页面,因此每个工作进程排队数百次渲染并达到 CPU 上限。
为什么按地址限制无济于事
速率限制统计每个地址的请求数。当数千个地址每个只发出一到三次请求时,没有任何单个地址接近限制。屏蔽国家会屏蔽我们的访客。爬虫难以廉价伪造的是像以前访问过该网站的浏览器那样行事。
在 Cloudflare 边缘:三条免费规则
免费版允许每个区域使用五条 WAF 自定义规则。我们使用了三条,全部采用托管质询而非直接屏蔽,因此被误判的人可以点击通过:
- 伪造浏览器:GET 请求的用户代理包含
Chrome/且没有Sec-Fetch-Mode标头。自 2019 年以来的每个 Chrome 版本都会发送该标头。 - 爬虫云:来自阿里云、腾讯云和华为云网络的页面请求,我们拦截的大部分流量来自这些网络。
- 紧急措施,默认关闭:完全没有 Cookie 且没有 Referer 的 GET 请求。曾访问过该网站或通过链接进入的人会顺利通过;不保存 Cookie 的爬虫会在每次请求时遇到质询。我们仅在洪流期间启用它,因为它也会质询那些在全新浏览器中输入地址的人。
这三个规则都会放行 Cloudflare 验证过的机器人、自报为机器人的用户代理、我们自己的地址、API 和 webhook 路径,以及非页面文件。两天后,流量洪峰仍在持续:仅一小时内,伪浏览器规则就挑战了超过 10,000 个请求。Cloudflare 每次查询返回的 10,000 个事件来自 9,627 个地址,其中没有任何一个地址的请求数超过 4 个。
在源站:应用中的一道关卡
无头浏览器部分拥有完整的浏览器请求头,因此在紧急规则关闭时,仅靠边缘规则无法拦截它。我们在应用本身中,在页面渲染器前放置了一个小型中间件:
- 一个带有 Chrome 用户代理但既无
Sec-Fetch-Mode也无客户端提示的请求,会在执行任何工作之前收到 403; - 一个陌生人,没有我们的任何 cookie 也没有 Referer,只有在 worker 有空闲容量时才会被服务,速率约为每秒几个页面。超过该速率,一个 400 字节的 503 页面会设置一个 cookie 并在两秒后重新加载。真人第二次尝试就能获得页面;不保存 cookie 的抓取器则永远无法获得。
在第一分钟内,该关卡拒绝了约 6,700 个伪浏览器,并挡下了约 6,700 个陌生人,同时以每秒约 9 个页面的速度服务陌生人。慢速请求降至零,worker 的 CPU 从满载降至 12% 到 15%,内存从每个 4 到 5 GB 降至 0.65 GB。
我们保持关闭的功能
- Bot Fight Mode。 它会挑战自动化流量,且在免费版中无法通过自定义规则跳过,它还拦截了我们支付提供商的 webhook。
- 整个区域的 Under Attack 模式。 它会让每个访客、API 客户端和 webhook 都经过中间页。紧急规则仅对陌生人的页面请求完成同样的工作。
- 免费版的速率限制规则。 它按地址计数,而这次流量洪峰从未触发它。
- 屏蔽 AI 爬虫。 我们希望被 AI 搜索助手发现,而这些爬虫并不是问题所在。
要点
通过住宅代理发起的流量洪峰很容易通过其缺失项来识别:浏览器 fetch 请求头、cookie 和 Referer。在边缘拦截它,将陌生人限制在 worker 能渲染的范围内,并确保服务器之外的某些东西能在页面停止打开时注意到。Night Watch 将这三者打包在一起:看门狗、带有紧急规则自动切换功能的规则,以及针对 Python Web 应用的关卡。