+− THE DAILY DIFFdev & AI news
SHIP IT

一个正则表达式搞垮了 Cloudflare,持续 27 分钟。

2019 年 7 月 2 日,UTC 时间 13:42:Cloudflare Web 应用程序防火墙的一条新规则在大约两秒钟内在全球 180 多个城市生效。

2019 年 7 月 2 日,UTC 时间 13:42:Cloudflare Web 应用程序防火墙的一条新规则在大约两秒钟内在全球 180 多个城市生效。这是一条模拟模式下的 XSS 规则,因此它不会阻止任何内容,但它仍然在每个请求上运行,并且以 .*(?:.*=.*) 结尾。PCRE 回溯,每个处理 HTTP 请求的 CPU 核心都达到 100%,每个由 Cloudflare 代理的网站都在 27 分钟内返回 502 错误;流量下降 82%。终止开关位于 Cloudflare Access 之后,而 Cloudflare Access 又位于 Cloudflare 之后。事后分析,来自 Cloudflare 自己的报告:数周前在旨在节省 CPU 的重构中移除的 CPU 保护措施,允许任何规则跳过测试阶段的程序,没有复杂性保证的引擎,11 个原因和 7 个修复方案。对修复方案的评价:SHIP IT。

阅读书面版(英文) ↗

本视频涵盖的内容

  • UTC 时间 13:31–13:42:PR 合并,CI 通过(无 CPU 测试),Quicksilver 将规则推送到 180 多个城市;WAF 规则跳过 DOG → PIG → Canary 阶段
  • 13:45–14:07:第一页;全球 CPU 100%,流量 −82%,到处都是 502 错误;内部控制面板位于 Cloudflare Access 之后,而 Cloudflare Access 已宕机;部分凭据已过期;一个不常演练的绕过方案
  • 14:07–14:09:全球 WAF 终止;27 分钟后流量和 CPU 恢复正常;14:52 WAF 恢复,但没有该规则
  • 7 月 2 日(UTC 时间 15:50)和 7 月 12 日:Cloudflare 发布了当日说明和完整的事后分析;重新添加了 CPU 保护措施,重新读取了 3,868 条规则,分阶段部署,转向线性时间正则表达式引擎

翻译的文字记录

译自英文原版旁白。可用音频和字幕由 YouTube 控制。

0:00 一个正则表达式在所有 Cloudflare 服务器上同时生效, 在接下来的二十七分钟里,它后面的网站都是 502 页面, 这对于防火墙来说,是可能的最严格的设置。 2019 年 7 月 2 日,UTC 时间 13:42。 Cloudflare 在两小时内发布:不是攻击,是错误部署, 流量下降 82%。 十天后,CTO John Graham-Cumming 发布了完整的事后分析, 包括正则表达式,Hacker News 给出了 698 分,

0:26 对于一次中断来说,这是一个起立鼓掌的成绩。 它如何发生,为何可能,以及谁真正应该承担责任。 这里是 The Daily Diff,事后分析。 13:31。一个拉取请求合并:一条新的防火墙规则,用于防范跨站 脚本攻击,处于模拟模式,因此它什么都不阻止。 13:37,测试通过;没有一个测试测量 CPU。 13:42,规则在两秒内推送到 180 个城市, 因为 WAF 规则跳过了其他发布会经历的 dog、pig 和 canary 阶段。

0:54 13:45,出现第一个页面。 13:49,Hacker News 上有一个关于状态页面的帖子, 上面仍然显示所有系统运行正常。 该规则以点星、点星、等于、点星结尾:任何内容, 然后是任何内容,然后是等号。 PCRE 贪婪地猜测,失败,然后回溯所有其他分割。 x 等于 x 需要 23 步。 等号后有二十个 x:555 步。

1:15 二十个 x,没有等号:4,067 步,什么也没找到。 在每个请求上运行,每个核心都达到百分之百, 彻底地什么都不做。 两个守卫本应该发现它。 数周前,规则的 CPU 限制在一次重构中被错误地移除, 这次重构旨在使 WAF 使用更少的 CPU。 而且该程序允许任何规则跳过测试阶段,因为规则是为了阻止实时 攻击而存在的;但这并不是紧急情况,它还是全球生效了。

1:37 14:00,WAF 被识别;不是攻击。 14:02,有人提议全球终止:一个组件, 关闭,全球范围。开关在 Cloudflare Access 之后。 Cloudflare Access 在 Cloudflare 之后。 一些凭据因未使用而过期,因此互联网上最快的网络 花了五分钟时间来绕过一个没有人演练过的方案。 14:07,终止。14:09,流量恢复正常。 git blame:单一速度的部署,全球范围;一个 CPU 保护措施被

2:04 意外重构移除;一个没有上限的正则表达式引擎。 不是编写规则的工程师:事后分析列出了 11 个原因, 没有指名道姓。影响范围:27 分钟,82% 的流量, 每个核心在每个城市都达到 100% CPU。 仪表盘和 API 位于同一个边缘之后,因此客户甚至无法关闭 它。Hacker News 上,事后分析的评论:他们有一个问题, 使用了一个正则表达式,现在他们有两个问题了。 老笑话了。仍然有效。

2:28 裁决,事后分析:SHIP IT。 CPU 保护措施回来了,所有 3,868 条规则都人工读取, 规则经过测试阶段,引擎转向 Ken Thompson 于 1968 年发布的 具有线性时间保证的引擎。 星期一:任何每个请求运行的东西都不能有点星点星, 并且将终止开关与它终止的东西分开。 给我发送你仍然不被允许谈论的事件, 在评论中,或者发送到 the daily diff dot dev。

2:53 这就是今天的 diff。 我是 Axrisi 的 Niko。 负责任地合并。

来源

  1. John Graham-Cumming, "Details of the Cloudflare outage on July 2, 2019" (Jul 12, 2019)blog.cloudflare.com
  2. Matthew Prince, "Cloudflare outage caused by bad software deploy (updated)" (Jul 2, 2019)blog.cloudflare.com
  3. Matthew Prince on X, Jul 2, 2019, 14:22 UTCx.com
  4. Matthew Prince on X, Jul 2, 2019, 14:36 UTC ("No evidence yet attack related")x.com
  5. Hacker News, Jul 2, 2019, 13:49 UTC — "Cloudflare Network Performance Issues" (631 points)news.ycombinator.com
  6. Hacker News, Jul 2, 2019 — "Cloudflare outage caused by bad software deploy" (348 points)news.ycombinator.com
  7. Hacker News, Jul 12, 2019 — "Details of the Cloudflare outage on July 2, 2019" (698 points)news.ycombinator.com
  8. TechCrunch, Jul 2, 2019techcrunch.com

相关视频

postmortem · zh-CN · 2026年9月19日

Google Cloud 因空白字段而崩溃。历时三小时。

一个包含几个空白字段的策略行触发了一个空指针,导致 Google Cloud 所有区域同时崩溃,随后 Cloudflare 也随之瘫痪。2025 年 6 月 12 日,世界标准时间 17:49:Service Control(批准每个 Google Cloud API 请求的二进制文件)读取了一个配额策略变更,其中包含“意外的空白字段”,触发了两周前发布的代码路径(没有空检查和功能标志),并进入了

2:57 ↗
postmortem · zh-CN · 2026年9月16日

Facebook 从互联网上删除了自己。六小时。

Facebook 从互联网上移除了自己的地址,当其工程师赶到现场恢复服务时,门禁读卡器也失灵了,因为它们也在 Facebook 上运行。2021 年 10 月 4 日,世界标准时间 15:40:一条例行的骨干网维护命令导致 Facebook 数据中心之间的所有链接中断;用于阻止该命令的审计工具存在一个错误;Facebook 的域名服务器,由于无法访问数据中心,根据设计撤回了它们自己的 BGP 路由

3:01 ↗
postmortem · zh-CN · 2026年9月22日

一次重启让Telstra回到了2006年。九百万部手机受到影响。

墨尔本的一名工程师在凌晨2:50重新启动了一个时序机箱,到早餐时间,澳大利亚最大的移动网络竟然认为现在是2006年11月。2026年7月8日:Telstra的NTP机箱中的一张GPS卡在电源维修期间重启,其固件从未进行过GPS周翻转更新,因此它选择了旧的纪元,回到了1024周之前。由于2025年10月的一个权宜之计,这张卡成为了网络中唯一的层级1(stratum-1)时间源——并且2020年切换到

3:15 ↗