The End of Incognito Detection in Chromium 147+: How a Decade-Old Privacy Leak Was Finally Patched + Video

Listen to this Post

Featured Image

Introduction:

For over ten years, websites have exploited subtle differences in browser storage behavior to detect whether users are browsing in incognito or private mode, enabling fingerprinting and tracking across sessions. The recent Chromium 147+ update fundamentally dismantled the most reliable detection vector by replacing dynamic storage quotas with a static value, plugging a privacy hole that allowed attackers to infer total disk size and uniquely identify devices even behind the same IP address.

Learning Objectives:

  • Understand how `navigator.storage.estimate()` was abused to detect incognito mode and leak disk capacity information.
  • Learn step-by-step methods to test incognito detection across Chromium, Firefox, and Safari using developer tools and custom scripts.
  • Implement defensive techniques to harden browser privacy and offensive testing using the detectIncognito library.

You Should Know:

  1. The Storage API Quota Leak: How Chromium Betrayed Your Disk Size

The core vulnerability in pre-147 Chromium browsers lay in the `estimate()` method of the Storage API. In normal browsing mode, browser data writes to disk, and the API returns a `quota` value representing approximately 60% of the user’s total available disk space. In incognito mode, however, all storage is written to RAM, resulting in a dramatically smaller `quota` (typically a few hundred megabytes). The difference between these two numbers was a binary signal—large quota = normal mode, small quota = incognito.

But the leak didn’t stop at detection. Because the exact quota value correlates with disk size, an attacker could fingerprint two identical MacBooks on the same Wi-Fi network by simply comparing the `quota` numbers. A MacBook with 256GB storage would report ~150GB quota, while a 512GB model would report ~300GB—enabling persistent tracking without cookies.

Step‑by‑step guide to test this leak (pre-Chromium 147):

  1. Open Chrome DevTools (F12) and go to the Console tab.
  2. Paste the following JavaScript to retrieve storage estimates:
    if (navigator.storage && navigator.storage.estimate) {
    navigator.storage.estimate().then(estimate => {
    console.log(<code>Quota: ${(estimate.quota / 10243).toFixed(2)} GB</code>);
    console.log(<code>Usage: ${(estimate.usage / 10243).toFixed(2)} GB</code>);
    });
    }
    
  3. Run the same code in a normal window and then in an incognito window.
  4. Compare the `quota` values. Prior to Chromium 147, incognito would show <1 GB; normal mode would show dozens or hundreds of GB.

Linux/Windows command to simulate browser storage behavior:

Use `curl` to inspect response headers and storage-related fields (though Storage API is client-side, server-side fingerprinting can combine with other leaks):

 Linux/macOS: Check disk size (potential side-channel correlation)
df -h /
 Windows PowerShell: Get disk size
Get-PSDrive C | Select-Object Used,Free
  1. The Patch: Static Quota as a Privacy Shield

Chromium 147+ resolved the issue by returning a static, fixed quota value regardless of browsing mode. No matter whether the browser is writing to disk or RAM, `estimate().quota` now returns a constant number (e.g., 2,147,483,647 bytes). This single change achieves two goals:
– Eliminates incognito detection via quota comparison.
– Removes the disk-size side channel, preventing device differentiation based on storage capacity.

Step‑by‑step guide to verify the patch:

  1. Update to Chromium 147 or later (Chrome 147+, Edge 147+, Brave 1.75+).

2. Open both normal and incognito windows.

3. Execute the same `navigator.storage.estimate()` code in both.

  1. Observe that both windows return identical `quota` values.

Verification using a minimal HTML test page:

<!DOCTYPE html>
<html>
<head><title>Incognito Test</title></head>
<body>
<button onclick="checkStorage()">Check Storage Quota</button>

<pre id="output"></pre>

<script>
function checkStorage() {
if (!navigator.storage || !navigator.storage.estimate) {
document.getElementById('output').innerText = 'Storage API not supported.';
return;
}
navigator.storage.estimate().then(estimate => {
const isIncognitoGuess = (estimate.quota < 2e9) ? 'Possible incognito (old method)' : 'Likely normal (or Chromium 147+)';
document.getElementById('output').innerText = 
`Quota: ${estimate.quota} bytes\nUsage: ${estimate.usage} bytes\n${isIncognitoGuess}`;
}).catch(err => {
document.getElementById('output').innerText = `Error: ${err.message}`;
});
}
</script>

</body>
</html>
  1. Cross-Browser Behavior: Firefox and Safari Still Leak (Differently)

Firefox and Safari never relied on quota sizes for incognito detection. Instead, they throw exceptions when certain Storage API operations are attempted in private mode. For instance, `navigator.storage.persist()` or accessing IndexedDB in private windows may reject with a security error. The `detectIncognito` library by Joe Rutkowski leverages this behavior:

  • Firefox/Safari: `navigator.storage.estimate()` works, but trying to request persistent storage fails. Error → incognito.
  • Chromium (pre-147): No error, but quota differs.
  • Chromium 147+: No error and quota is static → detection impossible.

Step‑by‑step guide to test cross‑browser detection:

1. Install the detectIncognito library from GitHub:

git clone https://github.com/Joe12387/detectIncognito.git
cd detectIncognito

2. Include `detectIncognito.js` in an HTML file:

<script src="detectIncognito.js"></script>

<script>
detectIncognito().then(result => {
console.log('Private mode:', result.isPrivate);
console.log('Browser:', result.browser);
});
</script>

3. Test in Firefox private window, Safari private tab, and Chrome incognito (version <147 vs >=147).

Linux command to launch browsers in private mode for testing:

 Linux
google-chrome --incognito &
firefox --private-window &
brave-browser --incognito &
 Windows (Command Prompt)
start chrome --incognito
start firefox -private-window
  1. Building a Custom Incognito Detector (For Legacy Browsers)

If you need to support older Chromium versions or understand the internals, here’s a minimal detection script that combines both quota and error-based methods.

JavaScript implementation:

async function detectIncognitoLegacy() {
// Method 1: Storage quota heuristic (Chromium <147)
if (navigator.storage && navigator.storage.estimate) {
const { quota } = await navigator.storage.estimate();
// Threshold: less than 1GB indicates likely incognito (pre-147)
if (quota < 1e9) return { isPrivate: true, method: 'quota' };
}

// Method 2: Persistent storage request (Firefox/Safari)
if (navigator.storage && navigator.storage.persist) {
try {
const isPersisted = await navigator.storage.persisted();
if (!isPersisted) {
const granted = await navigator.storage.persist();
if (!granted) return { isPrivate: true, method: 'persist' };
}
} catch (e) {
// Permission denied often means private mode
return { isPrivate: true, method: 'exception' };
}
}
return { isPrivate: false, method: 'none' };
}

// Usage
detectIncognitoLegacy().then(console.log);

5. Hardening Your Own Applications Against Fingerprinting

If you run a website or API, avoid relying on incognito detection for security decisions (e.g., blocking paywall bypass attempts). Instead, adopt privacy-respecting alternatives:

  • Use server-side session tracking with short-lived tokens.
  • Implement rate limiting based on IP + User‑Agent + behavioral heuristics (not storage quotas).
  • For anti-bot measures, consider CAPTCHA or WebAuthn instead of client-side storage probing.

API security example (Node.js/Express):

const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15  60  1000, // 15 minutes
max: 100, // limit each IP to 100 requests
keyGenerator: (req) => `${req.ip}-${req.headers['user-agent']}`
});
app.use('/api/sensitive', limiter);
  1. Cloud Hardening: Preventing Disk Size Inference in Virtual Environments

The same disk‑size leak concept applies to cloud environments where container or VM metadata might expose storage quotas. Attackers could infer instance types or account tiers based on reported block device sizes.

Mitigation steps for Linux cloud hosts:

  1. Mask `/proc/partitions` and `/sys/block//size` using seccomp or AppArmor for untrusted processes.
  2. Use tmpfs for ephemeral containers so that storage reports a small, uniform size.
  3. Apply a Linux kernel patch that returns fake disk sizes via LSM hooks (advanced).

Example command to check exposed disk size on a Linux VM:

lsblk -b | grep disk | awk '{print $4}'  prints disk size in bytes

To harden, run untrusted code inside a Docker container with a limited, fake overlay filesystem:

docker run --read-only --tmpfs /tmp:rw,noexec,nosuid,size=100M alpine df -h

What Undercode Say:

  • Key Takeaway 1: The Chromium 147+ patch eliminates a decade-old incognito detection vector, but it also removes an unintended fingerprinting surface that exposed disk sizes—improving privacy for all users.
  • Key Takeaway 2: Legacy detection methods still work on Firefox and Safari, making cross-browser privacy inconsistent; developers should avoid building critical logic around private mode detection.
  • Analysis: While this fix is a win for user privacy, it underscores a broader pattern: browsers often leak high-entropy system characteristics (disk size, CPU cores, installed fonts) that enable tracking beyond cookies. The storage quota leak was particularly egregious because it gave attackers a persistent hardware identifier without any user notification. Google’s decision to patch it only after a decade highlights the slow pace of privacy fixes when they aren’t exploited at scale. Future browser versions will likely continue locking down such side channels—expect similar changes to `navigator.hardwareConcurrency` and deviceMemory. For defenders, this means moving fingerprinting defenses server-side; for offensive researchers, new vectors like audio‑fingerprinting and CSS-based storage probing will gain traction.

Prediction:

As browser vendors progressively eliminate passive fingerprinting surfaces, attackers will pivot to active, user‑interaction‑based techniques (e.g., Canvas fingerprinting using WebGL, and behavior analysis via mouse movements). Within two years, we’ll see a major push for standardized “privacy budgets” in Chromium, where any API that returns more than a few bits of entropy triggers a permission prompt. The death of incognito detection is just the first domino—the next will be the deprecation of synchronous `navigator.plugins` and navigator.mimeTypes, followed by randomization of `navigator.userAgent` on every origin. Privacy may finally win, but at the cost of breaking legacy anti-fraud systems that depend on these leaks.

▶️ Related Video (76% Match):

https://www.youtube.com/watch?v=2VyOPHEOnDw

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Joe12387 After – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky