Skip to content

zugdurchfahrt/seed-noise-consistency

Repository files navigation

TL;DR: Research-grade anti-fingerprinting pipeline (Python + JavaScript) injecting deterministic, seed-based patches for Canvas/WebGL/WebGPU/Fonts/Headers via CDP.
Runs on Windows with VPN (ProtonVPN/OpenVPN) + optional proxy

Browser Anti-Fingerprinting: Python + JavaScript

Russian version: see Readme_RUS.md.

What this project is about

The system has been designed to evaluate and mitigate modern browser fingerprinting surfaces (Canvas 2D/OffscreenCanvas, WebGL/WebGPU, Fonts, UA-CH/Headers).

The project demonstrates that isolated fingerprint randomization is largely ineffective. Browser fingerprint mitigation only becomes meaningful when multiple observable surfaces are controlled as a consistent system rather than patched independently.

Architecture

Python (Selenium + undetected_chromedriver) + JavaScript patches (modules) injection via CDP to control fingerprint surfaces. Mitmproxy is optional and is switched directly in main.py.

Pipeline Contract Map

Architecture and ownership

Determinism and worker scopes

Public API patching

Runtime domains

  • WebGL critical paths - Maps WebGL values, hooks, and diagnostic routes.
  • Headers pipeline - Coordinates browser preferences, CDP, JavaScript, and optional mitmproxy headers.
  • Fonts compound - Documents generated font assets, runtime loading, Canvas interaction, and deterministic behavior.
  • RectsPatchModule is a window-realm module that influences DOM/SVG layout and rendering inputs, which are subsequently measured by native Chromium via DOMRect, DOMRectList, Element.getBoundingClientRect(), Element.getClientRects(), Range.getBoundingClientRect(), Range.getClientRects(), and SVG layout/geometry APIs.
  • Guard flag - Defines guard lifecycle.

Diagnostics

Ethics & scope

The tools are intended for testing, debugging and research purposes only. Do not use to bypass security controls or violate site policies or laws.
This is a privacy research tool and detector resilience tester. Remember that you are responsible for how you use it.

Important disclaimer

This is not a "silver bullet" or an anonymity toolkit. Modern FP techniques (TLS fingerprints, behavioral checks, WebGL/WebGPU constraints, AudioContext API updates, etc.) can correlate sessions and deanonymize environments even with patches. Partial control of the surface does not provide full anonymity.

Project goals

The primary objective of the project was to conduct a comprehensive examination of the browser from an internal perspective, with the aim of identifying the APIs in use and any data-leak channels that may be present.
Understand how fonts, WebGL, Client Hints, plug-ins and other components affect the fingerprint.
Maintain oversight of fingerprinting and its replication through profiling and network layer management.

Configuration & principles

There is no hardcoding: profile values are assembled from dictionaries/profile data and exposed through window.__* only as bootstrap/bridge inputs. The actual patch logic, helpers, and intermediate state are progressively moved into module-local closure state instead of being kept as long-lived public globals.
MDN/Chromium compatibility: hooks stay within native API boundaries, with closure-owned routing/helpers used to preserve receiver/owner contracts and avoid Illegal invocation.
__GLOBAL_SEED / DPR / device metrics: synchronized through initialization variables.

Project status

Research / non-commercial; released “as is”. No stability guarantees.
Built by a single author — scenario/OS coverage is limited. Forks and contributions are welcome.
See Issues/TODO for applicability limitations.
Executed only on Windows + ProtonVPN (OpenVPN CLI). Other OS/VPNs not tested.
In sum, the pipeline is being initialised and the script is being executed. The designated tasks are being carried out, just several surfaces have been patched.

License

The Unlicense (Public Domain). Copyright and related rights are waived
to the extent possible. You may copy, modify, publish, use, compile, sell,
and distribute, with or without attribution. Software is provided “AS IS”.

Requirements

OS: Windows 10/11 (OpenVPN path assumes Windows).
Python: 3.12 (3.11+ recommended).

3rd-party

OpenVPN installed locally (default path is set in vpn_utils.py, can be changed). mitmproxy (in requirements.txt, only needed when the mitmproxy switch is ON). Chrome/Chromium — local copy of Chrome for Testing path configured in main.py. All Python deps are pinned in requirements.txt.

Run modes

There is one entrypoint: main.py.

Mitmproxy is controlled by a small visible switch near the top of main.py:

MITMPROXY_ON = True
# MITMPROXY_OFF = True

This means: run with mitmproxy. main.py starts mitmdump, sends Chrome through 127.0.0.1:8082, and stops the process on exit.

To run without mitmproxy, flip the two lines:

# MITMPROXY_ON = True
MITMPROXY_OFF = True

In this mode, main.py does not start mitmdump, does not set Chrome proxy options, and Chrome connects directly.

Only one line must be active. If both are active or both are commented, startup fails immediately with a clear error.

With mitmproxy

✔ Easier browsing without immediate CORS/header challenges.
✔ Direct visibility into CORS/headers/Client Hints.
✖ Requires mitmproxy.
✖ Detectors may identify mitmproxy TLS fingerprint as not "native".

Without mitmproxy

✔ Simpler and lighter startup.
✔ No local proxy layer.
✖ CORS limitations may reduce effectiveness for real browsing; this mode is better as a direct browser/CDP/JS pipeline.

Note: VPN usage is enforced in both modes unless you disable the VPN calls separately.

Using without a built-in VPN client

The script can be run without controlling OpenVPN. In this case, you can:
✔ Use any other VPN client (including one controlled via a graphical interface).
✔ Or work without VPN at all.
To accomplish this, just comment calls to the VPNClient instance methods responsible for VPN authentication, setup, and connection in def main():
# client.verify()
# client.prepare()
# logger.info("Preparation completed")
# client.connect()
client.post()
In this mode, the script works exactly as before, performing all subsequent steps without stopping or starting OpenVPN processes.
If you already have a VPN set up in any other way, the script will simply use the current network environment.

Repository structure

Modules overview (short)

Python

  • main.py - main orchestration entrypoint: builds/loads profile data, prepares CDP injection payloads, launches Selenium + undetected-chromedriver, wires runtime helpers, and applies the staged JS pipeline to window and worker scopes.
  • profile_data_source - source dictionaries and base profile data for platform/browser composition: Win32/MacIntel shells, browser-version maps, plugin sets, permissions setting, and profile defaults.
  • rand_met.py - fonts pipeline: prepares per-platform generated fonts, cache metadata, and font_patch.generated.js from the Jinja2 template.
  • cdp_catapult.py - CDP-side payload assembly and ServiceWorker delivery helper used by the runtime injection flow.
  • cdp_worker_env.py - attaches to DedicatedWorker and SharedWorker targets over CDP and applies the same environment overrides used by the main page, keeping worker-side userAgent, language, languages, platform, and hardwareConcurrency accessor variables aligned before execution resumes.
  • handle_cors_addon.py - mitmproxy runtime addon for CORS/preflight handling, service-domain filtering, and request/response-side header coordination.
  • headers_adapter.py - realistic Accept/header shaping by browser brand/version.
  • helpers.py - shared runtime/profile helpers used by main.py and the injection pipeline.
  • vpn_utils.py - VPN lifecycle and region-aligned setup using .ovpn files from cfg_vpn.
  • overseer.py - Python-side logging/diagnostic helper.
  • core_window.js - provides the foundational layer for all other window-related modules and initializes the shared Core infrastructure. It contains the key runtime mechanisms: common wrappers, Core.applyTargets, safe descriptor installation, native shaping and toString masking, the invalid-this contract, and diagnostic utilities. It is also the place where safeDefine, the wrapper factory for method / accessor / ctor, and the logic for preserving the API's native-looking surface are implemented. Defines the contract-driven patching engine through Core.applyTargets. Downstream modules rely on it as the support layer preserving native behavior and appearance, correct handling of invalid receiver and other engine-level errors while maintaining the expected native pass-through semantics.
  • context.js - acts as an orchestration layer for Canvas/WebGL: it assembles FernwehHooks, validates the presence of hooks exported from respective modules, then registers them in a unified hook queue. It also acts as a patching gateway: it wraps getContext, toDataURL, toBlob, convertToBlob, CanvasRenderingContext2D methods, and WebGL prototypes so that downstream modules pass through a single point of application, preventing proxy leaks, this loss, and broken native descriptor mechanics.
  • prng_seed.js - gateway for __GLOBAL_SEED from the Python backend to the JavaScript environment. Module installs seed-driven PRNG state and exposes the deterministic seed context used downstream by graphics/media patches.
  • bootstrap_hide.js - initializes the internal bootstrap context and moves startup values out of the public window surface into private pipeline state. It creates and maintains FernwehContext, transfers bootstrap data into internal state objects, hides service fields from enumeration, and removes temporary global values once the required owners and retention snapshots are ready.
  • set_log.js - JS-side logging/diagnostic emitter. Creates a JS-side logger/diag buffer and a unified __DEGRADE__ channel.
  • probe.js - pipeline observability and self-checking layer. It validates runtime invariants, descriptor integrity, call semantics, and timeout behavior after patches are loaded.
  • wrk.js - worker-scope coordinator for environment propagation and patch installation across Dedicated/Shared/Service Workers.
  • WORKER_PATCH_SRC.js - worker-side patch source bundle consumed by bootstrap/prelude stages.
  • worker_bootstrap.js - early worker bootstrap glue that connects worker creation with the injected patch payload.
  • sw_prelude.js - Service Worker prelude used to establish the environment before worker patch application.
  • set_reflect.js - worker-side reflection/native-surface helper.

Generated files & templates

Fonts

Ready-to-use generated fonts are already included in assets/generated_fonts/Win32 and assets/generated_fonts/MacIntel, together with their generated font indexes. For a normal run, you do not need to add or move any font files.

The bundled fonts were taken from Google Fonts and then renamed by the project pipeline for runtime use. They are meant to make the project start without forcing every user to search for their own font set first.

If you want to use your own fonts, place only .woff2 files into assets/fonts_raw and run the project normally. During startup, rand_met.py treats assets/fonts_raw as an intake folder: it validates the files, skips unsuitable fonts, copies accepted files into the platform-specific generated folder, updates the index, and regenerates the runtime font patch. After a file is accepted, it is removed from that intake folder.

Custom fonts should be normal text fonts. Avoid icon fonts, emoji fonts, empty/broken files, fonts without basic ASCII coverage, and fonts with restrictive embedding flags or extreme metrics.

Logging

  • type in console to get:

  • L.PROBE(); probe.js log console output, json and html files saving;

  • L.DEGRADE.getBuffer(); - set_log.js console output;

  • set_log.js json log file saving:

    (() => {
      const json = JSON.stringify(L.__DEGRADE__?.getBuffer?.() || [], null, 2);
      const stamp = new Date().toISOString().replace(/[:.]/g, "-");
      const url = URL.createObjectURL(new Blob([json], { type: "application/json" }));
      const a = Object.assign(document.createElement("a"), { href: url, download: `degrade-buffer-${stamp}.json` });
      a.click();
      URL.revokeObjectURL(url);
    })();

You also can easily just type L.exportMyDebugLog() in Devtools console to get the same result.

Mitmproxy switch

Open main.py, choose one active profile, then press Play/F5 in VS Code.

With mitmproxy:

MITMPROXY_ON = True
# MITMPROXY_OFF = True

Without mitmproxy:

# MITMPROXY_ON = True
MITMPROXY_OFF = True

Quick start (Windows)

Install deps:

python -m venv venv
venv\Scripts\activate
pip install --upgrade pip
pip install -r requirements.txt

Put .ovpn files into cfg_vpn.

Fonts are already prepared in assets/generated_fonts; no font setup is required for a normal run. To add your own fonts, put .woff2 files into assets/fonts_raw and let the startup font pipeline process them.

Run main.py from VS Code or from the terminal. The active mitmproxy profile is selected inside main.py by the switch shown above.

  • If you face error "permission denied" during installation → run pip install --no-cache-dir -r requirements.txt

Issues/TODO

  • Implement TLS fingerprint rotation via OpenSSL.
  • Treat success/ready events from places that only record hook installation only as applied: the mechanism is installed, but the result is not yet proven.
  • For the font module, emit success only if the state is observable after the current chain through the DOM/CSS/font-measurement surface, not only through internal structures; otherwise mark applied_but_not_effective or emit no success.

About

Anti-fingerprinting browser pipeline (Python + JS) injecting deterministic, seed-based patches for Canvas/WebGL/WebGPU/Fonts/Headers via CDP

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Contributors