v151 · Storage · Privacy Leak Explainer
Privacy Leak Explainer
How the storage quota leak worked, why it was a reliable Incognito detector, and exactly what Chrome 151 changed to close it.
the attack
Incognito (Chrome < 151)
Quota = fraction of available RAM (e.g. 1/10th of 16 GiB ≈ 1.6 GiB capped at ~120 MB)
Normal mode (Chrome < 151)
Quota = max(10 GiB, ceil(disk_quota_GiB)) — always at or above the 10 GiB floor
Any script could call navigator.storage.estimate() and check if the quota was below 500 MB — with near certainty, that indicated Incognito mode.
This reads the same API surface the old detector used and compares the returned quota with the old fingerprint threshold and the 10 GiB floor Chrome 151 applies in both browsing modes.
// Detection script (worked on Chrome < 151)
const { quota } = await navigator.storage.estimate();
if (quota < 500 * 1024 * 1024) {
// quota < 500 MB → almost certainly Incognito mode
console.log('User is in Incognito');
}
why the leak existed
-
1
Predictable storage quota shipped. An earlier Chrome release bucketed the
estimate()quota for sites without unlimited storage permission to prevent fingerprinting via disk size:quota = max(10 GiB, ceil(actual_quota_GiB)) + usage -
!
Incognito uses RAM, not disk. Incognito mode sets
actual_quotato a fraction of available RAM rather than the disk-based value. On a typical 16 GB laptop, this is around 120 MB — far below the 10 GiB floor. The bucketing formula was supposed to apply a floor, but because the Incognito code path used a different quota source, the floor was never reached.Incognito: actual_quota ≈ 120 MB max(10 GiB, ceil(0.12 GiB)) = max(10 GiB, 1 GiB) → should be 10 GiB BUT: bug caused raw 120 MB to be returned instead -
!
Detection trivially reliable. Because the 120 MB value is derived from RAM (which varies per device but is always well below 10 GiB on any realistic machine), a simple threshold check of < 500 MB reliably identified Incognito with effectively 0 false positives.
-
✓
Chrome 151 fix: apply the same bucketing in Incognito. The Incognito quota source is changed so it uses the same disk-based static value that normal mode uses — the bucketing floor applies correctly in both modes. Sites calling
estimate()now see the same >10 GiB quota regardless of the browsing mode.Chrome 151: quota = max(10 GiB, ceil(disk_quota_GiB)) in both modes → Incognito is no longer distinguishable via estimate()
see also
- Quota Estimate Demo — run estimate() and see the fixed values live
- ChromeStatus entry
- MDN — StorageManager.estimate()
implementation reference
Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗