Fingerprinting and bot checks
Two goals pull in opposite directions. Fingerprinting protection wants the browser to look different on every site, so sites can't recognise the user. Bot checks (Google's "unusual traffic" page, Cloudflare's "Checking your browser", reCAPTCHA, hCaptcha) look precisely for browsers that don't look like ordinary Chrome, because automation tools are the ones that change things.
Changed in 1.5.0 JBrowser resolves this by changing one thing that matters most for tracking, doing it invisibly, and being indistinguishable from Chrome in everything else.
What JBrowser changes#
Canvas read-backs get per-site noise (privacy.fingerprint_protection, on by default). Drawing text or shapes
on a <canvas> and reading the pixels back is the strongest fingerprint a script can take, because it reveals the
exact GPU, driver and font rendering. privacy_js (engine/js.py) wraps
getImageData(), toDataURL() and toBlob() so the returned pixels have a few bits flipped:
- the noise is derived from a per-session seed (a new random number each time JBrowser starts) and the host, so one site sees stable values during a session (its own features keep working) while two sites see different values and can't correlate them;
- about 2 % of pixels change by one bit in one colour channel, invisible to people;
toDataURL()andtoBlob()work on a noisy copy, so the page's own canvas is never modified;- very large canvases (over 16 million pixels) are passed through untouched.
navigator.globalPrivacyControl and navigator.doNotTrack report the user's choice, matching the Sec-GPC
and DNT headers the request pipeline sends.
Doing it invisibly#
A script that replaces built-in functions is easy to spot: getImageData.toString() would show JavaScript source
instead of [native code], the replacement would have a prototype or the wrong length. privacy_js masks all of
that (page scripts). It is the only JBrowser
script in the page's own world; everything else runs in the isolated world, where pages can't see it.
What JBrowser deliberately leaves alone#
| Not changed | Why |
|---|---|
CPU cores (hardwareConcurrency), memory (deviceMemory) |
Workers report the real values and can't be patched; a mismatch between the page and a worker is a classic bot signal. |
| The WebGL vendor and renderer | A generic "ANGLE (Generic Renderer)" contradicts every other GPU detail and is itself unusual. |
| The user agent | Qt's QtWebEngine/x.y token is removed, so the user agent is exactly Chrome's. |
Accept-Language |
Built from the Windows display languages like Chrome's (en-AU,en;q=0.9). Qt sends none by default, which no real browser does. |
Sites that get no changes at all#
CHALLENGE_SITES in services/privacy.py lists the CAPTCHA and bot-check
providers and the big sign-in pages: Google (and every google.* domain), gstatic, reCAPTCHA, YouTube, hCaptcha,
Cloudflare, Arkose Labs / FunCaptcha, Microsoft sign-in (microsoftonline.com, live.com, microsoft.com), Apple
and iCloud, PayPal. On those sites privacy_js returns immediately, and CAPTCHA providers may keep their third-party
cookies (cookies). Sites on the user's allowed list are treated the same way.
If a site still shows a challenge#
- Check the site isn't blocked by a filter it depends on: switch protection off for it from the shield menu and reload. If that fixes it, the site belongs on the allowed list, or a filter exception is missing upstream.
- Check the network: a VPN, a shared IP or a proxy makes challenges far more likely, in any browser.
- For a CAPTCHA or sign-in provider missing from
CHALLENGE_SITES, add its registrable domain there.