Poketto

一条写坏的正则,把 Cloudflare 全球 CPU 吃光:服务中断约 27 分钟

文章主题概念插画

配图:AI 生成的概念示意图,非论文原图、真实现场或实验数据。

TL;DR:2019 年 7 月 2 日,Cloudflare 推送了一条 WAF(网页应用防火墙)新规则,其中的正则表达式发生灾难性回溯,全球处理 HTTP/HTTPS 的 CPU 几乎被打满,网站大面积返回 502,故障持续约 27 分钟。这不是黑客攻击,而是一次内部变更。Cloudflare 官方复盘强调,正则只是 11 个促成因素之一,并非唯一原因。

这是一起 2019 年的旧事故,不是最近的新闻,但它至今仍是“正则回溯”与“全球一次性发布”的经典教材。

一次“小改动”

按 Cloudflare 的复盘,事发当天世界协调时间 13:42,一次针对 XSS 检测的 WAF 托管规则更新开始部署。变更此前已经合并,测试也通过了,而且规则是以“模拟模式”上线的:只记录、不拦截。

问题在于,模拟模式依然会执行规则。规则里的正则核心部分可以简化为 .*.*=.*。两个相邻的 .* 让匹配引擎可以用大量方式切分同一段输入,匹配失败时就要把这些组合挨个试一遍,这就是回溯失控。复盘里给了个直观例子:输入 x= 后接 20 个 x,需要 555 步;20 个 x 但没有等号,则要 4067 步。真实流量更长、更杂,开销只会更大。

CPU 打满,流量骤降

规则一推到全球,各节点承担 HTTP/HTTPS 的 CPU 几乎都冲到接近 100%。部分前端服务器还有空闲核心,却因为连不上正在处理流量的进程而返回 502。Cloudflare 的工程师当时观察到流量大幅下跌,复盘称解决方案工程团队报告的流量损失约为八成。Cloudflare 自家的 Dashboard 和 API 同样跑在这套边缘网络上,客户一度也无法登录修改配置。

怎么止血

约 14:00 团队判断问题出在 WAF,并排除了攻击。14:07 执行了对 WAF 的“全局终止”,14:09 流量与 CPU 恢复正常。确认修复后,WAF 在 14:52 重新全面启用。

为什么说不只是一条正则

复盘列出了十余个促成因素,几个最关键的:

  • 数周前一次重构中,原本用来限制正则 CPU 消耗的保护措施被误删了。
  • 所使用的正则引擎没有运行时间保证。
  • 测试套件里没有检查 CPU 消耗。
  • 当时的运维流程允许规则不经分阶段放量、直接全球推送。
  • 回滚慢、状态页更新慢,内部系统登录也受影响。

后续

Cloudflare 恢复了 CPU 保护,人工检查了全部 3868 条 WAF 托管规则,计划给测试加入性能剖析,并改用 re2 或 Rust 这类有运行时间保证的正则引擎,规则发布也改为分阶段推进,全球紧急推送仅保留给正在发生的攻击。

教训很朴素:正则也是代码,会有性能 bug;而“只是模拟模式”的变更,同样可能让全球一起倒下。

来源:Details of the Cloudflare outage on July 2, 2019

关注这个空间,它的新文章会出现在你的关注动态里。