diff --git a/README.md b/README.md index 378e6ad..1498a15 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ Every website you visit is automatically placed in its own isolated container wi | Screen | Resolution, color depth, window dimensions | | Timezone | getTimezoneOffset, Date.toString, Intl.DateTimeFormat | | WebRTC | Forced relay-only ICE policy (blocks local IP leak) | -| Fonts | Noise on measureText + DOM element dimensions (offsetWidth/Height etc.) | -| Font API | document.fonts.check() blocked, size reports 0 | +| Fonts | Noise on measureText (prevents font enumeration) | +| Font API | document.fonts.check() returns uniform response | | ClientRects | Sub-pixel noise on getBoundingClientRect | | Plugins | Reports empty | | Battery | Always reports full/charging | diff --git a/inject.js b/inject.js index ba387c4..e1360e5 100644 --- a/inject.js +++ b/inject.js @@ -470,39 +470,13 @@ return metrics; }, pageWindow.CanvasRenderingContext2D.prototype, { defineAs: "measureText" }); - // --- DOM Element Dimension Noise --- - // Font enumeration measures offsetWidth/Height of test spans to detect installed fonts. - // Adding seeded noise prevents consistent dimension-based font fingerprinting. - const fontDimProps = ["offsetWidth", "offsetHeight", "scrollWidth", "scrollHeight", "clientWidth", "clientHeight"]; - for (const prop of fontDimProps) { - const origDesc = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, prop); - if (origDesc && origDesc.get) { - const origGet = origDesc.get; - Object.defineProperty(pageWindow.HTMLElement.prototype, prop, { - get: exportFunction(function() { - const val = origGet.call(this); - return val + (fontRng() - 0.5) * 0.3; - }, pageWindow), - configurable: true, - enumerable: true - }); - } - } - // --- document.fonts (FontFaceSet) API Protection --- - // document.fonts.check() directly reveals installed fonts; size/iterators expose count. + // document.fonts.check() always returns true so font loading logic works, + // but prevents enumeration of specific fonts by giving a uniform response. if (pageWindow.document.fonts) { try { Object.defineProperty(pageWindow.document.fonts, "check", { - value: exportFunction(function() { return false; }, pageWindow), - configurable: true, enumerable: true - }); - Object.defineProperty(pageWindow.document.fonts, "size", { - get: exportFunction(function() { return 0; }, pageWindow), - configurable: true, enumerable: true - }); - Object.defineProperty(pageWindow.document.fonts, "forEach", { - value: exportFunction(function() {}, pageWindow), + value: exportFunction(function() { return true; }, pageWindow), configurable: true, enumerable: true }); } catch(e) {} diff --git a/test/fingerprint-test.html b/test/fingerprint-test.html index 7d3a7df..da273e1 100644 --- a/test/fingerprint-test.html +++ b/test/fingerprint-test.html @@ -924,12 +924,13 @@ function testDocFonts() { report.docfonts = vals; let html = ""; - html += row("document.fonts.size", size, size === 0 ? "spoofed" : "real"); - html += row("check('16px Arial')", checkArial, !checkArial ? "spoofed" : "real"); - html += row("check('16px monospace')", checkMono, !checkMono ? "spoofed" : "real"); + html += row("document.fonts.size", size); + html += row("check('16px Arial')", checkArial, checkArial ? "spoofed" : "real"); + html += row("check('16px monospace')", checkMono, checkMono ? "spoofed" : "real"); el.innerHTML = html; - const spoofed = size === 0 && !checkArial && !checkMono; + // check() returning true for everything = spoofed (uniform response) + const spoofed = checkArial && checkMono; sectionStatus.docfonts = spoofed ? "pass" : "warn"; setDot("dot-docfonts", sectionStatus.docfonts); }