Hotdry.

Article

Firefox IndexedDB 排序算法指纹:跨会话识别的技术机制与量化评估

深入解析 Firefox indexedDB.databases() 排序算法如何产生稳定标识,量化跨会话识别率并给出工程级缓解策略。

2026-04-23security

在浏览器指纹识别技术持续演进的背景下,一个看似无害的 API 可能成为隐私防线的致命缺口。近期安全研究揭示了 Firefox 浏览器中 indexedDB.databases() API 的排序机制能够产生稳定且唯一的进程级标识符,这一发现对 Private Browsing 模式及 Tor Browser 的隔离承诺构成了直接挑战。本文将从技术实现层面剖析这一指纹生成机制,量化其跨会话识别能力,并给出可落地的缓解方案。

技术机制:排序如何演变为标识符

IndexedDB 是现代浏览器提供的客户端结构化数据存储 API,每个源(origin)可以创建一个或多个命名的数据库。indexedDB.databases() 方法返回当前源可见的数据库元数据,包括数据库名称和存储估算值。从设计初衷看,该 API 仅用于帮助开发者检查现有数据库、调试存储占用或管理应用状态,其返回结果的排序应当是中性的、annon-canonical 的。

然而,Firefox 的实现细节打破这一预期。问题根源位于 Gecko 渲染引擎的 IndexedDB 实现代码 dom/indexedDB/ActorsParent.cpp 中。在 Private Browsing 模式下,网站提供的数据库名称并不会直接用作磁盘标识符,而是通过全局哈希表 StorageDatabaseNameHashtable 映射为基于 UUID 的文件名基址。这层映射的核心逻辑在 GetDatabaseNameHashtable() 函数中执行,该函数在 OpenDatabaseOp::DoDatabaseWork() 内部被调用。

关键问题在于后续的枚举流程。当调用 indexedDB.databases() 时,Firefox 通过 QuotaClient::GetDatabaseFilenames(...) 收集数据库文件名,随后将这些基址插入到 nsTHashSet 哈希集合中。在返回结果之前,代码并未对哈希集合执行任何排序操作。最终返回的顺序取决于哈希表内部桶布局的迭代顺序,这是一个确定性但不可预测的函数,受 UUID 映射关系、哈希函数行为、哈希表容量以及插入历史等多重因素共同影响。由于 UUID 映射在 Firefox 进程存活期间保持稳定,同一浏览器实例在不同源下观察到的数据库顺序呈现高度一致性。

隐私影响:超越会话的持久追踪

这一机制产生的隐私后果远超表面所见。首先是跨源追踪能力的出现:两个完全无关的网站可以各自创建一组固定的命名数据库,调用 indexedDB.databases() 后比较返回顺序。如果两者观察到相同的排列组合,即可推断访问者使用同一 Firefox 进程。这种关联无需依赖 Cookie、LocalStorage 或任何显式的跨站通信通道,纯粹基于浏览器内部存储行为的确定性输出。

其次是私有会话边界的失效。在 Firefox Private Browsing 模式下,即便所有隐私窗口已关闭,只要 Firefox 进程本身仍在运行,先前会话产生的数据库顺序标识符并不会随之清除。这意味着一个网站可以在用户开启新一轮隐私浏览时识别出该用户曾在同一进程中活动过。对于 Tor Browser 而言,问题更为严重:Tor Browser 设计的 "New Identity" 功能本意是清除 Cookie、浏览历史并切换 Tor 电路,提供完全隔离的全新会话身份。然而,这一漏洞产生的稳定标识符在进程重启前持续有效,直接削弱了 "New Identity" 所能提供的隔离保证。

量化分析:识别率与熵值评估

评估这一指纹机制的实际威胁需要从熵值和识别容量两个维度进行量化。假设一个网站控制 N 个数据库名称,则可观察的排列组合数为 N!,理论熵值计算公式为 log2 (N!)。

当 N=16 时,理论熵值约为 44 位。这一数值在现实场景中足以区分全球范围内的并发浏览器实例。考虑到实际部署中网站可能采用更多受控数据库名称,实际可用熵值还存在进一步提升的空间。虽然内部哈希表行为可能降低可达排列数的理论上限,但这一点并不实质性地改变安全结论:暴露的排序仍然提供了远超常规识别需求的区分能力。

从实际识别率角度看,研究团队的实验表明,在受影响的 Firefox Private Browsing 和 Tor Browser 构建版本中,两个不同源在相同浏览器进程生命周期内观察到相同排列的概率接近 100%。该标识符在页面重载、新开隐私窗口甚至 "New Identity" 操作后保持不变,仅在完全重启浏览器时才会改变。这一特性使其成为极具威胁的长期追踪向量。

缓解策略:从漏洞到防护的工程路径

Mozilla 已在 Firefox 150 和 ESR 140.10.0 版本中发布修复,补丁对应 Mozilla Bug 2024220。修复的核心思路非常直接:阻止浏览器暴露反映进程级内部存储状态的熵源。具体方案是在返回结果前对数据库列表进行规范化排序,最简单可行的实现是按照数据库名称的字典序(lexicographic order)排列后输出。

这种修复方案具备多重优势。从概念复杂度角度看,排序操作易于理解和验证,不引入额外状态或随机性源。从兼容性角度看,排序结果对开发者而言可预测、可复现,不会破坏依赖确定性输出的合法使用场景。从工程实现角度看,只需在 GetDatabasesOp::DoDatabaseWork() 中增加一步排序调用即可完成修复,代码变更量极小。

对于暂未收到更新的 Firefox 分支或基于 Gecko 的第三方浏览器,研究团队建议采取以下临时缓解措施:在业务层面对 indexedDB.databases() 返回结果进行预处理 —— 创建一组已知数据库后检查返回顺序是否呈现非字母序排列,若发现异常排序模式则可判定存在指纹风险。用户层面可启用 Firefox 的 "Resist Fingering" 选项以降低暴露面,尽管这可能影响部分站点的正常功能。

工程启示:隐蔽实现的隐私代价

这一案例为浏览器安全工程提供了深刻教训。隐私漏洞并不总是来自对敏感数据的直接访问,更多时候源于内部实现细节的可观测输出。一个看似中性的 API 返回顺序,在特定实现条件下能够承载足以唯一标识设备或进程的熵值。开发者在设计浏览器 API 时需要始终假想恶意调用者的存在,对任何可能泄漏内部状态的输出进行规范化处理。

对于安全研究者和隐私工程师而言,这一发现再次印证了浏览器指纹识别技术的演进方向:传统的高 entropy 向量(如 User Agent、字体列表、Canvas 渲染)逐渐被防护机制压制后,攻击者正转向更加隐蔽的侧信道和实现细节。系统性审查浏览器各 API 的输出行为,建立自动化检测机制,将成为隐私保护工作下一阶段的重点方向。


资料来源:本文技术细节主要引自 Fingerprint 研究团队的公开披露报告,该漏洞已获 Mozilla 安全公告 MFSA2026-30 确认并发布修复补丁。

security