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:
sal
2026-03-01 00:50:30 -06:00
parent a371859273
commit 264a401cef
2 changed files with 135 additions and 76 deletions

View File

@@ -155,7 +155,10 @@
hardwareConcurrency: CONFIG.nav.hardwareConcurrency, hardwareConcurrency: CONFIG.nav.hardwareConcurrency,
platform: CONFIG.nav.platform, platform: CONFIG.nav.platform,
deviceMemory: CONFIG.nav.deviceMemory, 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)) { for (const [prop, value] of Object.entries(navOverrides)) {

View File

@@ -1,5 +1,6 @@
// Deterministic fingerprint profile generator // Deterministic fingerprint profile generator
// Given the same seed, always produces the same device identity // 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 // Real hardware values — spoofed values must NEVER match these
const REAL_HARDWARE = { const REAL_HARDWARE = {
@@ -8,6 +9,112 @@ const REAL_HARDWARE = {
screenHeight: 1080 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) { function generateFingerprintProfile(masterSeed) {
const rng = mulberry32(masterSeed); const rng = mulberry32(masterSeed);
@@ -15,14 +122,8 @@ function generateFingerprintProfile(masterSeed) {
return arr[Math.floor(rng() * arr.length)]; return arr[Math.floor(rng() * arr.length)];
} }
// Pick from array, but never the excluded value. Rerolls if needed. function pickExcluding(arr, excludeFn) {
function pickExcluding(arr, exclude) { const filtered = arr.filter(excludeFn);
const filtered = arr.filter(v => {
if (typeof exclude === "object" && exclude !== null) {
return Object.keys(exclude).some(k => v[k] !== exclude[k]);
}
return v !== exclude;
});
return filtered.length > 0 ? pick(filtered) : pick(arr); return filtered.length > 0 ? pick(filtered) : pick(arr);
} }
@@ -30,67 +131,19 @@ function generateFingerprintProfile(masterSeed) {
return (rng() * 0xFFFFFFFF) >>> 0; return (rng() * 0xFFFFFFFF) >>> 0;
} }
const platforms = ["Win32", "Linux x86_64", "MacIntel"]; // Pick a device archetype
const arch = pick(DEVICE_ARCHETYPES);
const vendors = [ // Pick coherent values from within the archetype
"Google Inc. (NVIDIA)", const res = pickExcluding(arch.resolutions, r =>
"Google Inc. (AMD)", r.width !== REAL_HARDWARE.screenWidth || r.height !== REAL_HARDWARE.screenHeight
"Google Inc. (Intel)", );
"Google Inc."
];
const renderers = [ const cores = pickExcluding(arch.cores, c => c !== REAL_HARDWARE.hardwareConcurrency);
"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 resolutions = [ const ffVersion = pick(FIREFOX_VERSIONS);
{ width: 2560, height: 1440 }, const userAgent = arch.uaTemplate.replace("{ffVersion}", ffVersion);
{ width: 1366, height: 768 }, const appVersion = arch.appVersionTemplate;
{ 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 });
return { return {
seed: masterSeed, seed: masterSeed,
@@ -99,22 +152,25 @@ function generateFingerprintProfile(masterSeed) {
fontSeed: subSeed(), fontSeed: subSeed(),
rectSeed: subSeed(), rectSeed: subSeed(),
nav: { nav: {
hardwareConcurrency: pick(hardwareConcurrencies), hardwareConcurrency: cores,
platform: pick(platforms), platform: arch.platform,
languages: pick(languageSets), languages: pick(LANGUAGE_SETS),
deviceMemory: pick(deviceMemories), deviceMemory: pick(arch.memory),
maxTouchPoints: 0 maxTouchPoints: 0,
userAgent: userAgent,
appVersion: appVersion,
oscpu: arch.oscpu
}, },
screen: { screen: {
width: res.width, width: res.width,
height: res.height, height: res.height,
colorDepth: pick(colorDepths) colorDepth: pick(COLOR_DEPTHS)
}, },
webgl: { webgl: {
vendor: pick(vendors), vendor: pick(arch.vendors),
renderer: pick(renderers) renderer: pick(arch.renderers)
}, },
timezone: pick(timezones), timezone: pick(TIMEZONES),
webrtc: { webrtc: {
blockLocal: true blockLocal: true
} }