Add HTTP header spoofing, fix timezone overrides, and expand fingerprint coverage
- Spoof User-Agent and Accept-Language HTTP headers per container via webRequest.onBeforeSendHeaders, eliminating JS/HTTP header mismatch - Wrap Intl.DateTimeFormat constructor to inject spoofed timezone - Pre-create timezone formatters outside exportFunction callbacks to avoid cross-compartment issues with Date.toString/toTimeString - Fix WebRTC relay-only config to use JSON serialization across Firefox compartment boundaries - Add new fingerprint vector protections: speechSynthesis.getVoices(), matchMedia screen dimension queries, performance.now() precision reduction, navigator.storage.estimate(), WebGL extension normalization - Add comprehensive fingerprint test page (test/fingerprint-test.html) covering all 17 vectors with per-container comparison support
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// Every site gets its own container. Auth redirects stay in the originating container.
|
||||
|
||||
const registeredScripts = {}; // cookieStoreId -> RegisteredContentScript
|
||||
const containerProfiles = {}; // cookieStoreId -> { userAgent, languages } for HTTP header spoofing
|
||||
let injectSourceCache = null;
|
||||
let domainMap = {}; // baseDomain -> cookieStoreId
|
||||
let pendingTabs = {}; // tabId -> true (tabs being redirected)
|
||||
@@ -86,6 +87,13 @@ async function buildProfileAndRegister(cookieStoreId, seed) {
|
||||
const profile = generateFingerprintProfile(seed);
|
||||
const vsStored = await browser.storage.local.get("vectorSettings");
|
||||
profile.vectors = vsStored.vectorSettings || {};
|
||||
|
||||
// Cache profile for HTTP header spoofing
|
||||
containerProfiles[cookieStoreId] = {
|
||||
userAgent: profile.nav.userAgent,
|
||||
languages: profile.nav.languages
|
||||
};
|
||||
|
||||
await registerForContainer(cookieStoreId, profile);
|
||||
}
|
||||
|
||||
@@ -337,11 +345,12 @@ async function handleResetAll() {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all storage
|
||||
// Clear all storage and caches
|
||||
domainMap = {};
|
||||
pendingTabs = {};
|
||||
cachedWhitelist = [];
|
||||
managedContainerIds.clear();
|
||||
for (const key of Object.keys(containerProfiles)) delete containerProfiles[key];
|
||||
await browser.storage.local.clear();
|
||||
|
||||
return { ok: true };
|
||||
@@ -408,6 +417,7 @@ async function handleSetVectorSettings(vectorSettings) {
|
||||
browser.contextualIdentities.onRemoved.addListener(async ({ contextualIdentity }) => {
|
||||
const cid = contextualIdentity.cookieStoreId;
|
||||
managedContainerIds.delete(cid);
|
||||
delete containerProfiles[cid];
|
||||
if (registeredScripts[cid]) {
|
||||
try { await registeredScripts[cid].unregister(); } catch(e) {}
|
||||
delete registeredScripts[cid];
|
||||
@@ -420,6 +430,40 @@ browser.contextualIdentities.onRemoved.addListener(async ({ contextualIdentity }
|
||||
await saveDomainMap();
|
||||
});
|
||||
|
||||
// --- HTTP Header Spoofing ---
|
||||
// Modifies User-Agent and Accept-Language headers to match each container's
|
||||
// spoofed profile, preventing server-side detection of JS/HTTP header mismatch.
|
||||
|
||||
function formatAcceptLanguage(languages) {
|
||||
if (!languages || languages.length === 0) return "en-US,en;q=0.5";
|
||||
return languages.map((lang, i) => {
|
||||
if (i === 0) return lang;
|
||||
const q = Math.max(0.1, 1 - i * 0.1).toFixed(1);
|
||||
return `${lang};q=${q}`;
|
||||
}).join(",");
|
||||
}
|
||||
|
||||
browser.webRequest.onBeforeSendHeaders.addListener(
|
||||
function(details) {
|
||||
// cookieStoreId is available in Firefox 77+ webRequest details
|
||||
const profile = containerProfiles[details.cookieStoreId];
|
||||
if (!profile) return {};
|
||||
|
||||
const headers = details.requestHeaders;
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
const name = headers[i].name.toLowerCase();
|
||||
if (name === "user-agent") {
|
||||
headers[i].value = profile.userAgent;
|
||||
} else if (name === "accept-language") {
|
||||
headers[i].value = formatAcceptLanguage(profile.languages);
|
||||
}
|
||||
}
|
||||
return { requestHeaders: headers };
|
||||
},
|
||||
{ urls: ["<all_urls>"] },
|
||||
["blocking", "requestHeaders"]
|
||||
);
|
||||
|
||||
// --- Init ---
|
||||
|
||||
async function init() {
|
||||
|
||||
Reference in New Issue
Block a user