Add coherent device profiles and User-Agent spoofing
Device archetypes ensure platform, GPU, resolution, and UA all match (e.g. Linux profile gets Mesa renderers and X11 UA string). Spoofs navigator.userAgent, appVersion, and oscpu per container.
This commit is contained in:
@@ -155,7 +155,10 @@
|
||||
hardwareConcurrency: CONFIG.nav.hardwareConcurrency,
|
||||
platform: CONFIG.nav.platform,
|
||||
deviceMemory: CONFIG.nav.deviceMemory,
|
||||
maxTouchPoints: CONFIG.nav.maxTouchPoints
|
||||
maxTouchPoints: CONFIG.nav.maxTouchPoints,
|
||||
userAgent: CONFIG.nav.userAgent,
|
||||
appVersion: CONFIG.nav.appVersion,
|
||||
oscpu: CONFIG.nav.oscpu
|
||||
};
|
||||
|
||||
for (const [prop, value] of Object.entries(navOverrides)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Deterministic fingerprint profile generator
|
||||
// Given the same seed, always produces the same device identity
|
||||
// Profiles are coherent — platform, UA, GPU, resolution all match
|
||||
|
||||
// Real hardware values — spoofed values must NEVER match these
|
||||
const REAL_HARDWARE = {
|
||||
@@ -8,6 +9,112 @@ const REAL_HARDWARE = {
|
||||
screenHeight: 1080
|
||||
};
|
||||
|
||||
// --- Device Archetypes ---
|
||||
// Each archetype defines a coherent set of values for a device class
|
||||
|
||||
const DEVICE_ARCHETYPES = [
|
||||
// Windows desktops
|
||||
{
|
||||
platform: "Win32",
|
||||
uaTemplate: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/{ffVersion}",
|
||||
appVersionTemplate: "5.0 (Windows)",
|
||||
oscpu: "Windows NT 10.0; Win64; x64",
|
||||
vendors: ["Google Inc. (NVIDIA)", "Google Inc. (AMD)", "Google Inc. (Intel)"],
|
||||
renderers: [
|
||||
"ANGLE (NVIDIA GeForce GTX 1060 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (AMD Radeon RX 6700 XT Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (Intel HD Graphics 630 Direct3D11 vs_5_0 ps_5_0)"
|
||||
],
|
||||
resolutions: [
|
||||
{ width: 2560, height: 1440 },
|
||||
{ width: 1366, height: 768 },
|
||||
{ width: 1536, height: 864 },
|
||||
{ width: 1680, height: 1050 },
|
||||
{ width: 2560, height: 1080 },
|
||||
{ width: 3440, height: 1440 },
|
||||
{ width: 1600, height: 900 }
|
||||
],
|
||||
cores: [4, 6, 8, 12, 16],
|
||||
memory: [8, 16, 32]
|
||||
},
|
||||
// Linux desktops
|
||||
{
|
||||
platform: "Linux x86_64",
|
||||
uaTemplate: "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/{ffVersion}",
|
||||
appVersionTemplate: "5.0 (X11)",
|
||||
oscpu: "Linux x86_64",
|
||||
vendors: ["Google Inc. (AMD)", "Google Inc. (Intel)", "Google Inc."],
|
||||
renderers: [
|
||||
"Mesa Intel(R) UHD Graphics 620",
|
||||
"Mesa AMD Radeon RX 580",
|
||||
"ANGLE (Intel, Mesa Intel(R) UHD Graphics 620, OpenGL 4.6)",
|
||||
"Mesa Intel(R) HD Graphics 530",
|
||||
"AMD Radeon RX 6600 (radeonsi, navi23, LLVM 15.0.7, DRM 3.49)"
|
||||
],
|
||||
resolutions: [
|
||||
{ width: 2560, height: 1440 },
|
||||
{ width: 1366, height: 768 },
|
||||
{ width: 1536, height: 864 },
|
||||
{ width: 1440, height: 900 },
|
||||
{ width: 3440, height: 1440 },
|
||||
{ width: 1600, height: 900 }
|
||||
],
|
||||
cores: [2, 6, 8, 12, 16],
|
||||
memory: [4, 8, 16, 32]
|
||||
},
|
||||
// macOS desktops
|
||||
{
|
||||
platform: "MacIntel",
|
||||
uaTemplate: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:128.0) Gecko/20100101 Firefox/{ffVersion}",
|
||||
appVersionTemplate: "5.0 (Macintosh)",
|
||||
oscpu: "Intel Mac OS X 10.15",
|
||||
vendors: ["Google Inc. (Apple)", "Google Inc. (Intel)", "Google Inc."],
|
||||
renderers: [
|
||||
"ANGLE (Apple, Apple M1, OpenGL 4.1)",
|
||||
"ANGLE (Apple, Apple M2, OpenGL 4.1)",
|
||||
"ANGLE (Intel, Intel(R) Iris(TM) Plus Graphics 655, OpenGL 4.1)",
|
||||
"ANGLE (Apple, ANGLE Metal Renderer: Apple M1 Pro, OpenGL 4.1)"
|
||||
],
|
||||
resolutions: [
|
||||
{ width: 2560, height: 1440 },
|
||||
{ width: 1440, height: 900 },
|
||||
{ width: 1680, height: 1050 },
|
||||
{ width: 2560, height: 1600 },
|
||||
{ width: 3024, height: 1964 }
|
||||
],
|
||||
cores: [4, 8, 10, 12],
|
||||
memory: [8, 16, 32]
|
||||
}
|
||||
];
|
||||
|
||||
const FIREFOX_VERSIONS = ["126.0", "127.0", "128.0", "129.0", "130.0", "131.0", "132.0", "133.0", "134.0"];
|
||||
|
||||
const LANGUAGE_SETS = [
|
||||
["en-US", "en"],
|
||||
["en-GB", "en"],
|
||||
["en-US"],
|
||||
["de-DE", "de", "en-US", "en"],
|
||||
["fr-FR", "fr", "en-US", "en"]
|
||||
];
|
||||
|
||||
const COLOR_DEPTHS = [24, 30, 32];
|
||||
|
||||
const TIMEZONES = [
|
||||
{ name: "America/New_York", offset: 300 },
|
||||
{ name: "America/Chicago", offset: 360 },
|
||||
{ name: "America/Denver", offset: 420 },
|
||||
{ name: "America/Los_Angeles", offset: 480 },
|
||||
{ name: "Europe/London", offset: 0 },
|
||||
{ name: "Europe/Berlin", offset: -60 },
|
||||
{ name: "Europe/Paris", offset: -60 },
|
||||
{ name: "Asia/Tokyo", offset: -540 },
|
||||
{ name: "Australia/Sydney", offset: -660 },
|
||||
{ name: "America/Toronto", offset: 300 },
|
||||
{ name: "America/Phoenix", offset: 420 }
|
||||
];
|
||||
|
||||
function generateFingerprintProfile(masterSeed) {
|
||||
const rng = mulberry32(masterSeed);
|
||||
|
||||
@@ -15,14 +122,8 @@ function generateFingerprintProfile(masterSeed) {
|
||||
return arr[Math.floor(rng() * arr.length)];
|
||||
}
|
||||
|
||||
// Pick from array, but never the excluded value. Rerolls if needed.
|
||||
function pickExcluding(arr, exclude) {
|
||||
const filtered = arr.filter(v => {
|
||||
if (typeof exclude === "object" && exclude !== null) {
|
||||
return Object.keys(exclude).some(k => v[k] !== exclude[k]);
|
||||
}
|
||||
return v !== exclude;
|
||||
});
|
||||
function pickExcluding(arr, excludeFn) {
|
||||
const filtered = arr.filter(excludeFn);
|
||||
return filtered.length > 0 ? pick(filtered) : pick(arr);
|
||||
}
|
||||
|
||||
@@ -30,67 +131,19 @@ function generateFingerprintProfile(masterSeed) {
|
||||
return (rng() * 0xFFFFFFFF) >>> 0;
|
||||
}
|
||||
|
||||
const platforms = ["Win32", "Linux x86_64", "MacIntel"];
|
||||
// Pick a device archetype
|
||||
const arch = pick(DEVICE_ARCHETYPES);
|
||||
|
||||
const vendors = [
|
||||
"Google Inc. (NVIDIA)",
|
||||
"Google Inc. (AMD)",
|
||||
"Google Inc. (Intel)",
|
||||
"Google Inc."
|
||||
];
|
||||
// Pick coherent values from within the archetype
|
||||
const res = pickExcluding(arch.resolutions, r =>
|
||||
r.width !== REAL_HARDWARE.screenWidth || r.height !== REAL_HARDWARE.screenHeight
|
||||
);
|
||||
|
||||
const renderers = [
|
||||
"ANGLE (NVIDIA GeForce GTX 1060 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (Intel HD Graphics 630 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"ANGLE (AMD Radeon RX 6700 XT Direct3D11 vs_5_0 ps_5_0)",
|
||||
"Mesa Intel(R) UHD Graphics 620",
|
||||
"Mesa AMD Radeon RX 580",
|
||||
"ANGLE (Intel, Mesa Intel(R) UHD Graphics 620, OpenGL 4.6)"
|
||||
];
|
||||
const cores = pickExcluding(arch.cores, c => c !== REAL_HARDWARE.hardwareConcurrency);
|
||||
|
||||
const resolutions = [
|
||||
{ width: 2560, height: 1440 },
|
||||
{ width: 1366, height: 768 },
|
||||
{ width: 1536, height: 864 },
|
||||
{ width: 1440, height: 900 },
|
||||
{ width: 1680, height: 1050 },
|
||||
{ width: 2560, height: 1080 },
|
||||
{ width: 3440, height: 1440 },
|
||||
{ width: 1600, height: 900 }
|
||||
];
|
||||
|
||||
const languageSets = [
|
||||
["en-US", "en"],
|
||||
["en-GB", "en"],
|
||||
["en-US"],
|
||||
["de-DE", "de", "en-US", "en"],
|
||||
["fr-FR", "fr", "en-US", "en"]
|
||||
];
|
||||
|
||||
// Exclude real hardwareConcurrency (4)
|
||||
const hardwareConcurrencies = [2, 6, 8, 12, 16];
|
||||
const deviceMemories = [4, 8, 16, 32];
|
||||
const colorDepths = [24, 30, 32];
|
||||
|
||||
// Timezones for spoofing — common real-world timezones
|
||||
const timezones = [
|
||||
{ name: "America/New_York", offset: 300 },
|
||||
{ name: "America/Chicago", offset: 360 },
|
||||
{ name: "America/Denver", offset: 420 },
|
||||
{ name: "America/Los_Angeles", offset: 480 },
|
||||
{ name: "Europe/London", offset: 0 },
|
||||
{ name: "Europe/Berlin", offset: -60 },
|
||||
{ name: "Europe/Paris", offset: -60 },
|
||||
{ name: "Asia/Tokyo", offset: -540 },
|
||||
{ name: "Australia/Sydney", offset: -660 },
|
||||
{ name: "America/Toronto", offset: 300 },
|
||||
{ name: "America/Phoenix", offset: 420 }
|
||||
];
|
||||
|
||||
// Resolution: never match real 1920x1080
|
||||
const res = pickExcluding(resolutions, { width: REAL_HARDWARE.screenWidth, height: REAL_HARDWARE.screenHeight });
|
||||
const ffVersion = pick(FIREFOX_VERSIONS);
|
||||
const userAgent = arch.uaTemplate.replace("{ffVersion}", ffVersion);
|
||||
const appVersion = arch.appVersionTemplate;
|
||||
|
||||
return {
|
||||
seed: masterSeed,
|
||||
@@ -99,22 +152,25 @@ function generateFingerprintProfile(masterSeed) {
|
||||
fontSeed: subSeed(),
|
||||
rectSeed: subSeed(),
|
||||
nav: {
|
||||
hardwareConcurrency: pick(hardwareConcurrencies),
|
||||
platform: pick(platforms),
|
||||
languages: pick(languageSets),
|
||||
deviceMemory: pick(deviceMemories),
|
||||
maxTouchPoints: 0
|
||||
hardwareConcurrency: cores,
|
||||
platform: arch.platform,
|
||||
languages: pick(LANGUAGE_SETS),
|
||||
deviceMemory: pick(arch.memory),
|
||||
maxTouchPoints: 0,
|
||||
userAgent: userAgent,
|
||||
appVersion: appVersion,
|
||||
oscpu: arch.oscpu
|
||||
},
|
||||
screen: {
|
||||
width: res.width,
|
||||
height: res.height,
|
||||
colorDepth: pick(colorDepths)
|
||||
colorDepth: pick(COLOR_DEPTHS)
|
||||
},
|
||||
webgl: {
|
||||
vendor: pick(vendors),
|
||||
renderer: pick(renderers)
|
||||
vendor: pick(arch.vendors),
|
||||
renderer: pick(arch.renderers)
|
||||
},
|
||||
timezone: pick(timezones),
|
||||
timezone: pick(TIMEZONES),
|
||||
webrtc: {
|
||||
blockLocal: true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user