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,74 +9,99 @@ const REAL_HARDWARE = {
screenHeight: 1080 screenHeight: 1080
}; };
function generateFingerprintProfile(masterSeed) { // --- Device Archetypes ---
const rng = mulberry32(masterSeed); // Each archetype defines a coherent set of values for a device class
function pick(arr) { const DEVICE_ARCHETYPES = [
return arr[Math.floor(rng() * arr.length)]; // Windows desktops
} {
platform: "Win32",
// Pick from array, but never the excluded value. Rerolls if needed. uaTemplate: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/{ffVersion}",
function pickExcluding(arr, exclude) { appVersionTemplate: "5.0 (Windows)",
const filtered = arr.filter(v => { oscpu: "Windows NT 10.0; Win64; x64",
if (typeof exclude === "object" && exclude !== null) { vendors: ["Google Inc. (NVIDIA)", "Google Inc. (AMD)", "Google Inc. (Intel)"],
return Object.keys(exclude).some(k => v[k] !== exclude[k]); renderers: [
}
return v !== exclude;
});
return filtered.length > 0 ? pick(filtered) : pick(arr);
}
function subSeed() {
return (rng() * 0xFFFFFFFF) >>> 0;
}
const platforms = ["Win32", "Linux x86_64", "MacIntel"];
const vendors = [
"Google Inc. (NVIDIA)",
"Google Inc. (AMD)",
"Google Inc. (Intel)",
"Google Inc."
];
const renderers = [
"ANGLE (NVIDIA GeForce GTX 1060 Direct3D11 vs_5_0 ps_5_0)", "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 (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 (AMD Radeon RX 6700 XT Direct3D11 vs_5_0 ps_5_0)",
"Mesa Intel(R) UHD Graphics 620", "ANGLE (Intel HD Graphics 630 Direct3D11 vs_5_0 ps_5_0)"
"Mesa AMD Radeon RX 580", ],
"ANGLE (Intel, Mesa Intel(R) UHD Graphics 620, OpenGL 4.6)" resolutions: [
];
const resolutions = [
{ width: 2560, height: 1440 }, { width: 2560, height: 1440 },
{ width: 1366, height: 768 }, { width: 1366, height: 768 },
{ width: 1536, height: 864 }, { width: 1536, height: 864 },
{ width: 1440, height: 900 },
{ width: 1680, height: 1050 }, { width: 1680, height: 1050 },
{ width: 2560, height: 1080 }, { width: 2560, height: 1080 },
{ width: 3440, height: 1440 }, { width: 3440, height: 1440 },
{ width: 1600, height: 900 } { 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 languageSets = [ 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-US", "en"],
["en-GB", "en"], ["en-GB", "en"],
["en-US"], ["en-US"],
["de-DE", "de", "en-US", "en"], ["de-DE", "de", "en-US", "en"],
["fr-FR", "fr", "en-US", "en"] ["fr-FR", "fr", "en-US", "en"]
]; ];
// Exclude real hardwareConcurrency (4) const COLOR_DEPTHS = [24, 30, 32];
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 = [
const timezones = [
{ name: "America/New_York", offset: 300 }, { name: "America/New_York", offset: 300 },
{ name: "America/Chicago", offset: 360 }, { name: "America/Chicago", offset: 360 },
{ name: "America/Denver", offset: 420 }, { name: "America/Denver", offset: 420 },
@@ -87,10 +113,37 @@ function generateFingerprintProfile(masterSeed) {
{ name: "Australia/Sydney", offset: -660 }, { name: "Australia/Sydney", offset: -660 },
{ name: "America/Toronto", offset: 300 }, { name: "America/Toronto", offset: 300 },
{ name: "America/Phoenix", offset: 420 } { name: "America/Phoenix", offset: 420 }
]; ];
// Resolution: never match real 1920x1080 function generateFingerprintProfile(masterSeed) {
const res = pickExcluding(resolutions, { width: REAL_HARDWARE.screenWidth, height: REAL_HARDWARE.screenHeight }); const rng = mulberry32(masterSeed);
function pick(arr) {
return arr[Math.floor(rng() * arr.length)];
}
function pickExcluding(arr, excludeFn) {
const filtered = arr.filter(excludeFn);
return filtered.length > 0 ? pick(filtered) : pick(arr);
}
function subSeed() {
return (rng() * 0xFFFFFFFF) >>> 0;
}
// Pick a device archetype
const arch = pick(DEVICE_ARCHETYPES);
// Pick coherent values from within the archetype
const res = pickExcluding(arch.resolutions, r =>
r.width !== REAL_HARDWARE.screenWidth || r.height !== REAL_HARDWARE.screenHeight
);
const cores = pickExcluding(arch.cores, c => c !== REAL_HARDWARE.hardwareConcurrency);
const ffVersion = pick(FIREFOX_VERSIONS);
const userAgent = arch.uaTemplate.replace("{ffVersion}", ffVersion);
const appVersion = arch.appVersionTemplate;
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
} }