Poketto

一个 == 没写成 >=:Cloudflare 把别人的密码和聊天记录泄进了搜索引擎缓存

文章主题概念插画

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

TL;DR:2017 年 2 月,Cloudflare 的 HTML 解析代码里有一处缓冲区越界读取,导致部分响应里夹带了其他网站用户的内存数据。Google、Bing 等搜索引擎还把其中一些内容缓存了下来。Cloudflare 称受影响的请求比例极低,约三百三十万分之一,并称没有发现被恶意利用的证据。客户的 SSL 私钥没有泄露,但 cookie、令牌等数据确实出现在了泄露内容里。

这是 2017 年的旧事,不是今天的新闻。

2017 年 2 月 17 日前后,Google Project Zero 的研究员 Tavis Ormandy 在做其他工作时,发现有些网页返回的内容里混着莫名其妙的乱码,里面有 HTTP 头、cookie,甚至是别人的私密消息。他顺着线索查到,问题出在 Cloudflare 的边缘服务器上。Cloudflare 在 2 月 18 日 UTC 零点多收到了漏洞细节。

根因很朴素。Cloudflare 用 Ragel 写了一个 HTML 解析器,Ragel 生成的代码在判断是否到达缓冲区末尾时,用的是“指针等于结尾”(==),而不是“指针大于等于结尾”(>=)。一旦指针因为某个错误分支多走一步,越过结尾,这个检查就永远不会触发,程序会继续往后读,把相邻的内存当作网页内容输出。Cloudflare 自己的说明是,出错的是自家的解析代码,不是 Ragel。

触发条件也很苛刻:页面末尾要有一个没写完的标签,比如 <script type=。Cloudflare 估计约 0.06% 的网站有这类标签。这个隐患潜伏了很久,直到 2017 年 2 月 13 日邮件地址混淆功能被部分迁移到新解析器,改变了缓冲区处理方式,才真正“活”过来。

影响最大的时段是 2 月 13 日到 18 日,约每 330 万个 HTTP 请求里有 1 个可能泄露内存。比例很小,但 Cloudflare 的流量太大。泄露的内容可能包含 HTTP 头、POST 数据片段、API 返回的 JSON、URI 参数、cookie 和各类令牌,一个站点的数据可能出现在另一个站点的响应里。

更麻烦的是缓存。这些带着别人数据的响应被搜索引擎抓取并保存。Cloudflare 与 Google、Yahoo、Bing 等逐一沟通,找到了 770 个含有泄露内容的缓存 URI,分属 161 个域名,并清理了这些缓存。他们还在 Pastebin 等站点上搜索过,没有发现相关内容。

处置速度不慢:邮件混淆功能在 2 月 18 日 UTC 01:19 被全球关闭,距收到细节 47 分钟;全球范围的缓解措施在不到 7 小时内完成。2 月 20 日,修复补丁全球上线,2 月 21 日三项相关功能恢复。Cloudflare 的公开事故报告发布于 2 月 23 日,此前一直等搜索引擎缓存清理完毕才公开。

这件事的教训并不新鲜:处理不可信输入的代码,边界检查宁可多写一点,也别依赖“应该不会走到那里”。同时,一个“只是顺便启用”的小功能,也可能让整个代理层成为泄密通道。

来源:Cloudflare:Incident report on memory leak caused by Cloudflare parser bug

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