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,
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)) {

View File

@@ -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,74 +9,99 @@ const REAL_HARDWARE = {
screenHeight: 1080
};
function generateFingerprintProfile(masterSeed) {
const rng = mulberry32(masterSeed);
// --- Device Archetypes ---
// Each archetype defines a coherent set of values for a device class
function pick(arr) {
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;
});
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 = [
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 (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 580 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 = [
"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: 1440, height: 900 },
{ 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 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-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];
const COLOR_DEPTHS = [24, 30, 32];
// Timezones for spoofing — common real-world timezones
const timezones = [
const TIMEZONES = [
{ name: "America/New_York", offset: 300 },
{ name: "America/Chicago", offset: 360 },
{ name: "America/Denver", offset: 420 },
@@ -87,10 +113,37 @@ function generateFingerprintProfile(masterSeed) {
{ 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 });
function generateFingerprintProfile(masterSeed) {
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 {
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
}