Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/anti-fingerprint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,14 @@ jobs:
run: |
cd lw
echo "$TOR_ESR_VERSION" > version
# Tor mode omits the bearbrowser anti-fp patches (CanvasTextMetrics +
# WebAudioFarble) — they are DISABLED to match the Tor Browser cohort, so
# compiling them in is pointless, and these 150-authored patches reject on
# the 140 ESR tree anyway (the default 150 build keeps them). So the
# Tor-mode ESR stack is the LibreWolf stack only, matching what
# apply-sourceos-overlays.sh --profile tor-mode injects (i.e. nothing).
echo "tor-mode stack: $(grep -c . assets/patches.txt) LibreWolf patches against firefox-$(cat version) (anti-fp patches omitted by design)"
# Tor mode omits the canvas/audio anti-fp patches (disabled to match the
# cohort; they're 150-authored and reject on 140 anyway) — but it DOES
# need the OS-spoof patch: forcing the Windows identity is a cohort
# requirement. Register only that one, matching apply-sourceos-overlays.sh
# --profile tor-mode.
cp ../overlay/gecko-patches/anti-fingerprint/anti-fp-tor-os-spoof.patch patches/
echo "patches/anti-fp-tor-os-spoof.patch" >> assets/patches.txt
echo "tor-mode stack: $(grep -c . assets/patches.txt) patches (LibreWolf + OS-spoof) against firefox-$(cat version)"
- name: Fetch ESR source
run: |
cd lw
Expand Down
31 changes: 21 additions & 10 deletions docs/tor-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,28 @@ value.** Three cases:
> You cannot be *uniquely-best-BearBrowser* and *network-anonymous* at the same
> time. You pick per session. That's the honest physics of it.

## §OS spoof — the biggest lever (needs a patch, not a pref)
## §OS spoof — the biggest lever (a compile-time patch)
Tor Browser presents `Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0)
Gecko/20100101 Firefox/140.0` for **all** desktop platforms. RFP computes its own
UA and **ignores** `general.useragent.override`, so this cannot be a pref — it is an
`nsRFPService` patch across ~8 use-sites (UA, platform, oscpu, appVersion, worker
equivalents, maxTouchPoints). Fully specified in
`gecko-patches/anti-fingerprint/anti-fp-tor-os-spoof.SPEC.md`; the `tor-mode`
profile already sets the trigger pref `bearbrowser.tor-mode.spoof-os=windows`
(a no-op until the patch lands). It is a *coordinated* change — one missed site =
an inconsistent identity that is worse than no spoof — so it is authored with a
compiler in the loop, not blind.
Gecko/20100101 Firefox/140.0` for **all** desktop platforms, so Mac/Linux users
hide in the Windows majority. RFP computes its own UA and **ignores**
`general.useragent.override`, so this must be a patch.

The patch (`gecko-patches/anti-fingerprint/anti-fp-tor-os-spoof.patch`, **applies
cleanly to firefox-140.12.0** — verified locally + in CI) turned out far smaller
than first specced. Every spoofed value except `navigator.platform` flows from the
`SPOOFED_*` macros in `nsRFPService.h` — `navigator.userAgent` (nsRFPService),
`navigator.oscpu`/`appVersion` (Navigator + WorkerNavigator), and the worker copies
(RuntimeService calls `Navigator::Get*`). So **one `#ifdef` in the header** forces
UA + oscpu + appVersion consistently, and `navigator.platform` (the one hardcoded
site) gets a one-line condition. No per-site coordination, no runtime pref, no
StaticPrefs codegen — so no inconsistent-identity risk.

It is gated on `-DBEARBROWSER_FORCE_WIN_SPOOF`, which `apply-sourceos-overlays.sh
--profile tor-mode` sets in the workspace mozconfig and injects the patch. The
default 150 build never defines it → real OS. `maxTouchPoints` stays 0 (Tor desktop)
rather than Windows' 10. Compile-verification lands with the Forgejo build; the
SPEC file (`anti-fp-tor-os-spoof.SPEC.md`) documented the original ~8-site runtime
approach and is now superseded by this simpler compile-time patch.

## §version — ride the 140 line, not 150
Default BearBrowser is **Firefox 150**; the live Tor cohort is **Firefox 140 ESR**.
Expand Down
52 changes: 52 additions & 0 deletions gecko-patches/anti-fingerprint/anti-fp-tor-os-spoof.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
BearBrowser Tor mode — force the Windows identity on all desktop platforms.

Tor Browser presents `Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0)
Gecko/20100101 Firefox/140.0` for EVERY desktop platform, so Mac/Linux users hide
in the Windows majority. Stock RFP keeps the real OS family, which would split our
Mac/Linux Tor-mode users into a tiny distinct cohort.

This is done at COMPILE time, gated on -DBEARBROWSER_FORCE_WIN_SPOOF, which the
tor-mode build sets in its mozconfig (apply-sourceos-overlays.sh --profile
tor-mode). The default 150 build does NOT define it, so it reports the real OS.

Because navigator.userAgent (nsRFPService), navigator.oscpu / navigator.appVersion
(Navigator + WorkerNavigator), and the worker copies (RuntimeService calls
Navigator::Get*) all flow from the SPOOFED_* macros in nsRFPService.h, one #ifdef
in the header forces UA + oscpu + appVersion consistently. navigator.platform is
the only value not macro-driven (hardcoded per-platform in Navigator::GetPlatform),
so it gets a one-line condition. MAX_TOUCH_POINTS stays 0 — Tor desktop reports 0,
not Windows' 10.

diff --git a/toolkit/components/resistfingerprinting/nsRFPService.h b/toolkit/components/resistfingerprinting/nsRFPService.h
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -38,7 +38,16 @@
// the platform-specific definitions.
#define SPOOFED_UA_OS_OTHER "X11; Linux x86_64"

-#ifdef XP_WIN
+// BearBrowser Tor mode: present the Windows identity on ALL desktop platforms so
+// Mac/Linux users hide in the Windows majority (the Tor Browser cohort). Enabled
+// at compile time via -DBEARBROWSER_FORCE_WIN_SPOOF (tor-mode build only).
+// MAX_TOUCH_POINTS stays 0 — Tor desktop reports 0, not Windows' 10.
+#ifdef BEARBROWSER_FORCE_WIN_SPOOF
+# define SPOOFED_UA_OS "Windows NT 10.0; Win64; x64"
+# define SPOOFED_APPVERSION "5.0 (Windows)"
+# define SPOOFED_OSCPU "Windows NT 10.0; Win64; x64"
+# define SPOOFED_MAX_TOUCH_POINTS 0
+#elif defined(XP_WIN)
# define SPOOFED_UA_OS "Windows NT 10.0; Win64; x64"
# define SPOOFED_APPVERSION "5.0 (Windows)"
# define SPOOFED_OSCPU "Windows NT 10.0; Win64; x64"
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2005,7 +2005,7 @@
}
}

-#if defined(WIN32)
+#if defined(WIN32) || defined(BEARBROWSER_FORCE_WIN_SPOOF)
aPlatform.AssignLiteral("Win32");
#elif defined(XP_MACOSX)
// Always return "MacIntel", even on ARM64 macOS like Safari does.
24 changes: 23 additions & 1 deletion scripts/apply-sourceos-overlays.sh
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,29 @@ done
afp_dir="$repo_root/gecko-patches/anti-fingerprint"
patches_txt="$workspace/source/assets/patches.txt"
if [ "$profile" = "tor-mode" ]; then
echo "feature-layer: tor-mode omits bearbrowser anti-fp patches (disabled to match cohort; see docs/tor-mode.md)"
echo "feature-layer: tor-mode omits the canvas/audio anti-fp patches (disabled to match cohort; see docs/tor-mode.md)"
# ...but tor-mode DOES need the OS-spoof patch: Tor forces the Windows identity
# on all desktop platforms, which is a cohort REQUIREMENT (not one of our extra
# protections). It's gated on -DBEARBROWSER_FORCE_WIN_SPOOF, set below.
osspoof="anti-fp-tor-os-spoof.patch"
if [ -f "$afp_dir/$osspoof" ] && [ -f "$patches_txt" ]; then
mkdir -p "$workspace/source/patches"
cp "$afp_dir/$osspoof" "$workspace/source/patches/$osspoof"
grep -qxF "patches/$osspoof" "$patches_txt" || echo "patches/$osspoof" >> "$patches_txt"
echo "feature-layer: registered tor-mode OS-spoof patch $osspoof"
fi
# Turn the spoof on at compile time. The macro gates nsRFPService.h + Navigator
# .cpp so the whole identity (UA/oscpu/appVersion/platform, main + workers)
# reports Windows. The default 150 build never sets it -> real OS.
mozcfg="$workspace/source/assets/mozconfig"
if [ -f "$mozcfg" ] && ! grep -q "BEARBROWSER_FORCE_WIN_SPOOF" "$mozcfg"; then
{
echo ''
echo '# BearBrowser Tor mode: present the Windows identity (Tor Browser cohort).'
echo 'export CXXFLAGS="${CXXFLAGS} -DBEARBROWSER_FORCE_WIN_SPOOF"'
} >> "$mozcfg"
echo "feature-layer: tor-mode mozconfig sets -DBEARBROWSER_FORCE_WIN_SPOOF"
fi
elif [ -d "$afp_dir" ] && [ -f "$patches_txt" ]; then
mkdir -p "$workspace/source/patches"
for p in anti-fp-canvas-text-metrics.patch anti-fp-audio.patch; do
Expand Down
9 changes: 4 additions & 5 deletions settings/profiles/tor-mode/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,10 @@ user_pref("webgl.enable-debug-renderer-info", false); // mask real GPU vendor/r
//
// (c) The BIG one: Tor makes EVERY desktop platform report Windows so Mac/Linux
// users hide in the Windows majority. RFP computes its own UA and IGNORES
// general.useragent/platform/oscpu.override, so this CANNOT be a pref — it needs
// the nsRFPService OS-spoof patch. This pref is the trigger that patch reads; it is
// a NO-OP until the patch lands. See
// gecko-patches/anti-fingerprint/anti-fp-tor-os-spoof.SPEC.md.
user_pref("bearbrowser.tor-mode.spoof-os", "windows");
// general.useragent/platform/oscpu.override, so this is NOT a pref — it is the
// compile-time OS-spoof patch (gecko-patches/anti-fingerprint/
// anti-fp-tor-os-spoof.patch), built in via -DBEARBROWSER_FORCE_WIN_SPOOF which
// apply-sourceos-overlays.sh --profile tor-mode sets. Nothing to set here.
//
// Match Tor Browser's first-party isolation posture.
user_pref("privacy.firstparty.isolate", false); // dFPI supersedes; keep consistent
Expand Down
Loading