我们发现了一个稳定的 Firefox 标识符,它关联了您所有的私人 Tor 身份。
We found a stable Firefox identifier linking all your private Tor identities

原始链接: https://fingerprint.com/blog/firefox-tor-indexeddb-privacy-vulnerability/

最近发现的安全漏洞影响了所有基于Firefox的浏览器,包括Tor浏览器。网站可以利用`indexedDB.databases()` API,根据IndexedDB数据库返回的顺序生成一个独特的、稳定的浏览器进程标识符——即使在私密浏览模式下也是如此。这使得跨站跟踪无需使用cookie,并且关键的是,破坏了Tor浏览器的“新身份”功能,将原本应该隔离的会话联系起来。 问题源于从内部存储结构派生的数据库名称的确定性排序,这种排序在浏览器运行时会持续存在。该标识符并非特定于源站,这意味着不同的网站可以检测到相同的浏览器实例并关联用户活动。该漏洞提供了一个令人惊讶的高容量指纹(在16个数据库的情况下,可能达到44位)。 Mozilla已在Firefox版本150和ESR 140.10.0(Bug 2024220)中迅速解决了此缺陷,方法是规范化API返回结果的顺序。此修复以及其他基于Gecko的浏览器的必要缓解措施,可以防止泄露进程级状态并恢复预期的隐私边界。这凸显了仔细考虑实现细节的重要性,因为看似无害的API如果暴露了稳定、内部的信息,就可能成为跟踪媒介。

## Firefox & Tor 身份泄露事件 研究发现 Firefox 中存在一个稳定的标识符,可能将看似私密的 Tor 身份关联起来,由 fingerprint.com 发现。该公司负责任地向 Mozilla 披露了此问题,尽管他们的业务重点是指纹识别——这一举动受到了评论员的质疑,他们指出竞争对手通常会私下利用漏洞。 讨论的中心是该漏洞的影响。虽然它在浏览器重启后不会持续存在,但一些人认为,即使是数据的临时关联对于拥有大量元信息的实体来说也是有价值的。 Tor 项目迅速解决了这个问题,在披露后的第二天发布了 Tor Browser 15.0.10 版本。用户们讨论了缓解策略,一些人建议禁用 JavaScript,但另一些人指出这*会增加*指纹识别,因为这会使用户更加独特。 Qubes OS/Whonix 用户认为他们的系统不会受到影响,因为他们为每个任务使用单独的虚拟机,并且新的 Tor 身份永远不会在同一个虚拟机中创建。
相关文章

原文

We recently discovered a privacy vulnerability affecting all Firefox-based browsers. The issue allows websites to derive a unique, deterministic, and stable process-lifetime identifier from the order of entries returned by IndexedDB, even in contexts where users expect stronger isolation.

This means a website can create a set of IndexedDB databases, inspect the returned ordering, and use that ordering as a fingerprint for the running browser process. Because the behavior is process-scoped rather than origin-scoped, unrelated websites can independently observe the same identifier and link activity across origins during the same browser runtime. In Firefox Private Browsing mode, the identifier can also persist after all private windows are closed, as long as the Firefox process remains running. In Tor Browser, the stable identifier persists even through the "New Identity" feature, which is designed to be a full reset that clears cookies and browser history and uses new Tor circuits. The feature is described as being for users who "want to prevent [their] subsequent browser activity from being linkable to what [they] were doing before." This vulnerability effectively defeats the isolation guarantees users rely on for unlinkability.

We responsibly disclosed the issue to Mozilla and to the Tor Project. Mozilla has quickly released the fix in Firefox 150 and ESR 140.10.0, and the patch is tracked in Mozilla Bug 2024220. The underlying root cause is inherited by Tor Browser through Gecko’s IndexedDB implementation, so the issue is relevant to both products and to all Firefox-based browsers.

The fix is straightforward in principle: the browser should not expose internal storage ordering that reflects process-scoped state. Canonicalizing or sorting results before returning them removes the entropy and prevents this API from acting as a stable identifier.

Why this matters

Private browsing modes and privacy-focused browsers are designed to reduce websites' ability to identify users across contexts. Users generally expect two things:

First, unrelated websites should not be able to tell they are interacting with the same browser instance unless a shared storage or explicit identity mechanism is involved.

Second, when a private session ends, the state associated with that session should disappear.

This issue breaks both expectations. A website does not need cookies, localStorage, or any explicit cross-site channel. Instead, it can rely on the browser’s own internal storage behavior to derive a high-capacity identifier from the ordering of database names returned by an API.

For developers, this is a useful reminder that privacy bugs do not always come from direct access to identifying data. Sometimes they come from deterministic exposure of internal implementation details.

For security and product stakeholders, the key point is simple: even an API that appears harmless can become a cross-site tracking vector if it leaks stable process-level state.

What is IndexedDB and what does indexedDB.databases() do?

IndexedDB is a browser API for storing structured data on the client side. Web applications use it for offline support, caching, session state, and other local storage needs. Each origin can create one or more named databases, which can hold object stores and large amounts of data.

The indexedDB.databases() API returns metadata about the databases visible to the current origin. In practice, developers might use it to inspect existing databases, debug storage usage, or manage application state.

Under normal privacy expectations, the order of results returned by this API should not, in itself, carry identifying information. It should simply reflect a neutral, canonical, or otherwise non-sensitive presentation of database metadata.

The issue we found comes from the fact that, in all Firefox-based browsers, the returned order was not neutral at all.

How indexedDB.databases() became a stable identifier

In all Firefox Private Browsing mode, indexedDB.databases() returns database metadata in an order derived from internal storage structures rather than from database creation order.

The relevant implementation is in dom/indexedDB/ActorsParent.cpp.

In Private Browsing mode, database names are not used directly as on-disk identifiers. Instead, they are mapped to UUID-based filename bases via a global hash table:

using StorageDatabaseNameHashtable = nsTHashMap<nsString, nsString>;
StaticAutoPtr<StorageDatabaseNameHashtable> gStorageDatabaseNameHashtable;

The mapping is performed inside GetDatabaseFilenameBase() called within OpenDatabaseOp::DoDatabaseWork().

When aIsPrivate is true, the website-provided database name is replaced with a generated UUID and stored in the global StorageDatabaseNameHashtable. This mapping:

  • Is keyed only by the database name string
  • Persists for the lifetime of the IndexedDB QuotaClient
  • Is shared across all origins
  • Is cleared only when Firefox is fully restarted

Later, when indexedDB.databases() is invoked, Firefox gathers database filenames via QuotaClient::GetDatabaseFilenames(...) called in GetDatabasesOp::DoDatabaseWork().

Database base names are inserted into an nsTHashSet.

No sorting is performed before iteration. The final result order is determined by iteration over the hash set’s internal bucket layout.

Because UUID mappings are stable for the lifetime of the Firefox process, and hash table structure and iteration order are deterministic for a given internal layout, the returned ordering becomes a deterministic function of the generated UUID values, hash function behavior, and hash table capacity and insertion history. This ordering persists across tabs and private windows, resetting only upon a full Firefox restart. Crucially, the UUID mapping and hash set iteration are not origin-scoped. They are process-scoped.

Reproducing the issue

A simple proof of concept is enough to demonstrate the behavior. Two different origins host the same script. Each script:

  1. Creates a fixed set of named databases.
  2. Calls indexedDB.databases().
  3. Extracts and prints the returned order.

In affected Firefox Private Browsing and Tor Browser builds, both origins observe the same permutation during the lifetime of the same browser process. Restarting the browser changes the permutation.

Conceptually, the output looks like this:

created:
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p

listed:
g,c,p,a,l,f,n,d,j,b,o,h,e,m,i,k

The important point is not the exact order itself, but rather that the order is not the original creation order, that the same order appears across unrelated origins, and it persists across reloads and new private windows, even after all private windows are closed. Only a full browser restart yields a new one. That is exactly what you do not want from a privacy perspective.

Privacy impact

This issue enables both cross-origin and same-origin tracking within a single browser runtime.

Cross-origin impact

Unrelated websites can independently derive the same identifier and infer that they are interacting with the same running Firefox or Tor Browser process. That lets them link activity across domains without cookies or other shared storage.

Same-origin impact

In Firefox Private Browsing mode, the identifier can persist even after all private windows are closed, provided the Firefox process itself is still running. That means a site can recognize a later visit in what appears to be a fresh private session. In Tor Browser, the stable identifier effectively defeats Tor Browser’s “New Identity” isolation within a running browser process, allowing websites to link sessions that are expected to be fully isolated from one another.

Why this is especially serious in Tor Browser

Tor Browser is specifically designed to reduce cross-site linkability and minimize browser-instance-level identity. A stable process-lifetime identifier cuts directly against that design goal. Even if it only survives until a full process restart, that is still enough to weaken unlinkability during active use.

Entropy and fingerprinting capacity

The signal is not just stable. It also has high capacity.

If a site controls N database names, then the number of possible observable permutations is N!, with theoretical entropy of log2(N!). With 16 controlled names, the theoretical space is about 44 bits. That is far more than enough to distinguish realistic numbers of concurrent browser instances in practice.

The exact number of reachable permutations may be somewhat lower because of internal hash table behavior, but that does not materially change the security story. The exposed ordering still provides more than enough entropy to act as a strong identifier.

The fix

The right fix is to stop exposing entropy derived from the internal storage layout.

The cleanest mitigation is to return results in a canonical order, such as lexicographic sorting. That preserves the API's usefulness for developers while removing the fingerprinting signal. Randomizing output per call could also hide the stable ordering, but sorting is simpler, more predictable, and easier for developers to reason about.

From a security engineering standpoint, an ideal fix:

  • Low conceptual complexity
  • Minimal compatibility risk
  • Direct elimination of the privacy leak

Responsible disclosure

We responsibly disclosed the issue to Mozilla and to the Tor Project. Mozilla has released the fix in Firefox 150 and ESR 140.10.0, and the patch is tracked in Mozilla Bug 2024220. Because the behavior originates from Gecko’s IndexedDB implementation, downstream Gecko-based browsers, including Tor Browser, are also affected unless they apply their own mitigation.

Building for privacy

This vulnerability shows how a small implementation detail can create a meaningful privacy problem. The impact is significant. Unrelated websites can link activity across origins during the same browser runtime, and private-session boundaries are weakened because the identifier survives longer than users would expect.

The good news is that the fix is simple and effective. By canonicalizing the output before returning it, browsers can eliminate this source of entropy and restore the expected privacy boundary. This is exactly the kind of issue worth paying attention to: subtle, easy to miss, and highly instructive for anyone building privacy-sensitive browser features.

联系我们 contact @ memedata.com