fix(js): move navigator fingerprint props onto Navigator.prototype (#388)
* Move navigator fingerprint props onto Navigator.prototype
Spoofed navigator properties (userAgent, appVersion, platform, language,
languages, plugins, mimeTypes, hardwareConcurrency, deviceMemory, webdriver,
share, canShare) lived as own-instance accessors/data props on the navigator
literal, which pixelscan's Navigator group flags via hasOwnProperty /
getOwnPropertyDescriptor. Real Chrome keeps them on Navigator.prototype.
Generalize the existing webdriver prototype IIFE to define every spoofed
accessor on a thin prototype one hop above Navigator.prototype, so
navigator.hasOwnProperty('platform') === false while navigator instanceof
Navigator stays true. Getters read the __obscura_* globals lazily (snapshot
vs per-page timing) and are _markNative'd so toString reports [native code].
plugins/mimeTypes are cached singletons.
hardwareConcurrency/deviceMemory now back onto __obscura_hw/__obscura_mem
globals written by __obscura_init, avoiding own data props. Those two globals
are pre-declared in _preHideInternals so the snapshot-time hide list captures
them and they stay invisible to reflection APIs.
* Tighten comments on navigator prototype fingerprint setup
Drop the long narrative blocks in bootstrap.js; keep short notes on why
props sit on the prototype hop, plugin caching, and hw/mem globals.
This commit is contained in:
1 parent
367601b1ca
commit
87fefef1ec
1 file changed
+60
-33
Vendored
+60
-33
@@ -15,6 +15,7 @@
|
||||
'__obscura_objects', '__obscura_oid', '__obscura_ua',
|
||||
'__obscura_platform', '__obscura_ua_platform', '__obscura_ua_platform_version',
|
||||
'__obscura_stealth', '__obscura_markTrusted',
|
||||
'__obscura_hw', '__obscura_mem',
|
||||
'__documentReadyState__', '__currentUrl',
|
||||
// internal helpers (var-declared throughout the file)
|
||||
'__processDynScriptQueue', '_markNative', '_fpRand', '_fpNoise',
|
||||
@@ -3019,32 +3020,15 @@ function _uaBrands() {
|
||||
return [ordered[p[0]], ordered[p[1]], ordered[p[2]]];
|
||||
}
|
||||
|
||||
// Fingerprint surfaces (UA, plugins, webdriver, etc.) live on the prototype
|
||||
// hop below, not as own props here: own accessors are a bot tell.
|
||||
globalThis.navigator = {
|
||||
get userAgent() { return globalThis.__obscura_ua || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"; },
|
||||
get appVersion() { return this.userAgent.replace('Mozilla/', ''); },
|
||||
language: "en-US", languages: ["en-US","en"], get platform() { return globalThis.__obscura_platform || "Win32"; },
|
||||
onLine: true, cookieEnabled: true, hardwareConcurrency: 8,
|
||||
onLine: true, cookieEnabled: true,
|
||||
maxTouchPoints: 0,
|
||||
vendor: "Google Inc.", product: "Gecko", productSub: "20030107",
|
||||
doNotTrack: null,
|
||||
deviceMemory: 8,
|
||||
connection: new NetworkInformation(),
|
||||
pdfViewerEnabled: true,
|
||||
get plugins() {
|
||||
return new PluginArray([
|
||||
new Plugin("PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("Chrome PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("Chromium PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("Microsoft Edge PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("WebKit built-in PDF", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
]);
|
||||
},
|
||||
get mimeTypes() {
|
||||
return new MimeTypeArray([
|
||||
new MimeType("application/pdf", "Portable Document Format", "pdf", null),
|
||||
new MimeType("text/pdf", "Portable Document Format", "pdf", null),
|
||||
]);
|
||||
},
|
||||
userAgentData: {
|
||||
mobile: false,
|
||||
get brands() { return _uaBrands(); },
|
||||
@@ -3131,16 +3115,61 @@ globalThis.navigator = {
|
||||
},
|
||||
};
|
||||
|
||||
// Move navigator.webdriver off the instance and onto a thin prototype so that
|
||||
// Object.getOwnPropertyDescriptor(navigator, 'webdriver') returns undefined,
|
||||
// matching Chrome where the property lives on Navigator.prototype.
|
||||
// Use Navigator.prototype as the chain base so navigator instanceof Navigator === true.
|
||||
// Put spoofed navigator props on a thin prototype above Navigator.prototype
|
||||
// so hasOwnProperty/getOwnPropertyDescriptor on the instance match Chrome.
|
||||
// Getters read __obscura_* lazily (snapshot vs per-page) and are _markNative'd.
|
||||
(function() {
|
||||
var _wdGetter = function() { return false; };
|
||||
_markNative(_wdGetter);
|
||||
var _wdProto = Object.create(Navigator.prototype);
|
||||
Object.defineProperty(_wdProto, 'webdriver', {get: _wdGetter, set: undefined, enumerable: true, configurable: true});
|
||||
Object.setPrototypeOf(globalThis.navigator, _wdProto);
|
||||
var _navProto = Object.create(Navigator.prototype);
|
||||
|
||||
function defGetter(key, fn) {
|
||||
_markNative(fn);
|
||||
Object.defineProperty(_navProto, key, {
|
||||
get: fn, set: undefined, enumerable: true, configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
defGetter('webdriver', function() { return false; });
|
||||
defGetter('userAgent', function() {
|
||||
return globalThis.__obscura_ua ||
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
|
||||
"(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36";
|
||||
});
|
||||
defGetter('appVersion', function() {
|
||||
return (globalThis.__obscura_ua ||
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
|
||||
"(KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36").replace('Mozilla/', '');
|
||||
});
|
||||
defGetter('platform', function() {
|
||||
return globalThis.__obscura_platform || "Win32";
|
||||
});
|
||||
defGetter('language', function() { return "en-US"; });
|
||||
defGetter('languages', function() { return ["en-US", "en"]; });
|
||||
|
||||
// Cache plugins/mimeTypes so navigator.plugins === navigator.plugins.
|
||||
var _plugins = new PluginArray([
|
||||
new Plugin("PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("Chrome PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("Chromium PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("Microsoft Edge PDF Viewer", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
new Plugin("WebKit built-in PDF", "internal-pdf-viewer", "Portable Document Format", []),
|
||||
]);
|
||||
var _mimeTypes = new MimeTypeArray([
|
||||
new MimeType("application/pdf", "Portable Document Format", "pdf", null),
|
||||
new MimeType("text/pdf", "Portable Document Format", "pdf", null),
|
||||
]);
|
||||
defGetter('plugins', function() { return _plugins; });
|
||||
defGetter('mimeTypes', function() { return _mimeTypes; });
|
||||
|
||||
// Values set per-page by __obscura_init (avoids own data props on navigator).
|
||||
defGetter('hardwareConcurrency', function() { return globalThis.__obscura_hw || 8; });
|
||||
defGetter('deviceMemory', function() { return globalThis.__obscura_mem || 8; });
|
||||
|
||||
_navProto.share = _markNative(function share(data) {
|
||||
return Promise.reject(new DOMException('Not allowed', 'NotAllowedError'));
|
||||
});
|
||||
_navProto.canShare = _markNative(function canShare() { return false; });
|
||||
|
||||
Object.setPrototypeOf(globalThis.navigator, _navProto);
|
||||
})();
|
||||
|
||||
globalThis.chrome = {
|
||||
@@ -6672,8 +6701,6 @@ navigator.keyboard = {
|
||||
};
|
||||
navigator.gpu = { requestAdapter() { return Promise.resolve(null); } };
|
||||
navigator.wakeLock = { request() { return Promise.reject(new DOMException('Not allowed', 'NotAllowedError')); } };
|
||||
navigator.share = function(data) { return Promise.reject(new DOMException('Not allowed', 'NotAllowedError')); };
|
||||
navigator.canShare = function() { return false; };
|
||||
|
||||
globalThis.opener = null;
|
||||
|
||||
@@ -7597,9 +7624,9 @@ globalThis.__obscura_init = function() {
|
||||
globalThis.outerWidth = sw; globalThis.outerHeight = sh - 40;
|
||||
|
||||
var hwValues = globalThis.__obscura_stealth ? [4, 6, 8, 12, 16] : [2, 4, 6, 8, 12, 16];
|
||||
globalThis.navigator.hardwareConcurrency = hwValues[Math.floor(_fpRand(400) * hwValues.length)];
|
||||
globalThis.__obscura_hw = hwValues[Math.floor(_fpRand(400) * hwValues.length)];
|
||||
var memValues = globalThis.__obscura_stealth ? [4, 8] : [0.25, 0.5, 1, 2, 4, 8];
|
||||
globalThis.navigator.deviceMemory = memValues[Math.floor(_fpRand(401) * memValues.length)];
|
||||
globalThis.__obscura_mem = memValues[Math.floor(_fpRand(401) * memValues.length)];
|
||||
|
||||
const t0 = Date.now() + Math.floor(_fpRand(641) * 100) - 50;
|
||||
globalThis.performance.timeOrigin = t0;
|
||||
|
||||
Reference in new issue
Block a user