Firefox 修了一个看起来很小、实际不轻的隐私漏洞。研究人员发现,indexedDB.databases() 在 Firefox/Gecko 里的返回顺序会泄露一个进程级稳定标识。结果是,不同网站能在同一浏览器进程运行期间,把你认成同一个人。
这事最扎眼的地方,不是网站读到了数据库内容,而是“不可关联”这层承诺被实现细节打穿了。好消息也很直接:Mozilla 已在 Firefox 150 和 ESR 140.10.0 修补,方法也够干脆,就是把结果规范化排序,去掉这份不该有的熵。
漏洞是什么,影响谁,现在修没修
问题出在 Firefox/Gecko 的 IndexedDB 实现,不是通用 Web 平台问题,也不是所有浏览器都中招。
研究人员的结论很明确:indexedDB.databases() 返回顺序会受到 Gecko 内部哈希/UUID 映射影响。这个映射是进程级的,不是站点级的。所以只要浏览器进程没彻底退出,不同站点就可能观察到同一个稳定模式。
受影响的重点对象有两类:
- 使用 Firefox 隐私浏览的人
- 使用 Tor Browser 的人
前者的问题是,私密窗口看着像新会话,但如果主进程还活着,可关联痕迹可能还在。后者的问题更敏感:Tor Browser 的 New Identity 目标就是切断前后会话关联,但这个 Gecko 同源缺陷会削弱这层边界。
修复状态已经明确:
| 项目 | 情况 |
|---|---|
| 漏洞范围 | Firefox-based / Gecko 系实现 |
| 标识生命周期 | 同一浏览器进程运行期间稳定,重启后变化 |
| 受影响产品 | Firefox 系浏览器,Tor Browser 受同源缺陷影响 |
| 修复版本 | Firefox 150、ESR 140.10.0 |
| 修复思路 | 对返回结果做规范化/排序,去掉熵源 |
这里要卡住一个边界:这不是 Tor 网络层被攻破,也不是匿名路由失效。问题发生在本地浏览器实现层,后果是会话可关联性被削弱,而不是匿名网络本身被破解。
一个“无害 API”的返回顺序,为什么能变成指纹
根子并不玄。Firefox 在私密场景下不会直接用站点给的数据库名做底层标识,而是映射成 UUID,并存进 Gecko 的全局哈希表。这个映射活在进程里,不跟站点边界走。
麻烦出在下一步:indexedDB.databases() 取回数据库元数据时,没有先做稳定排序,而是把内部结构的遍历顺序直接暴露给网页。网页看不到你的数据库内容,却能看到“这台浏览器此刻内部是怎么排的”。这就够了。
研究人员给出的量化也说明问题不小。如果网站可控 16 个数据库名,理论上可提供约 44 bit 的指纹容量。现实里未必总能跑满,但对“同一进程里把前后访问串起来”已经够强。
这也是它对隐私模式和 Tor 特别致命的原因。普通追踪里,网站常靠 Cookie、LocalStorage、登录态。这里不一样。即使这些传统标识不用,单靠返回顺序,也可能把两段看似断开的访问重新缝上。
“千里之堤,溃于蚁穴”放在这里不算夸张。早期浏览器指纹抓字体、Canvas、缓存侧信道,套路都类似:不是偷敏感数据,而是偷实现留下的秩序。本案只是换成了 IndexedDB 的返回顺序。
真正暴露的,是浏览器隐私工程的老毛病
我更在意的,不是这个洞本身,而是它暴露出的工程习惯:浏览器内部状态,经常会在不经意间变成网页可观测信号。
这类问题最难防的地方,不在权限弹窗,也不在 API 名字听起来危险不危险。恰恰相反,很多洞都藏在“看起来很普通”的接口里。数据库顺序、缓存命中、计时差异、渲染细节,过去都出过事。今天换了一个入口,逻辑没变。
所以这事不能轻描淡写成“小 API 漏洞”。对追踪行业来说,只要能稳定关联,就够用了。对隐私产品来说,只要边界没有按承诺生效,信誉就会掉分。用户买的不是某个函数名安全,而是“这次新会话,真的和上次断开”。
Mozilla 这次反应倒是该给分。问题根子清楚,修法也直接:把结果排序,别让内部哈希顺序外泄。没有兜圈子,也没有把风险说轻。浏览器厂商能在这类实现级隐私问题上又快又准,并不常见。
对普通 Firefox 用户,动作很简单:确认自己是否已升级到 Firefox 150 或 ESR 140.10.0 及后续版本。没升级前,如果你把隐私模式当隔离边界,最稳妥的临时做法是彻底退出浏览器进程,而不是只关窗口。
对 Tor Browser 用户和依赖匿名隔离工作的群体,重点不是恐慌换工具,而是先看所用版本是否已纳入 Gecko 修复。如果还没有,New Identity 的隔离效果就不能按最理想情况理解,至少要把“彻底重启进程”当成更保守的做法。
接下来真正该观察的,不是这个洞会不会被说成世界末日。公开材料目前给的是可行性和机理,不是大规模在野利用数据。更该盯的是 Gecko 里还有没有同类接口,把内部状态继续漏成外部信号。修掉一个点不难,把它当成一类问题来清,才难。
