gotchaModerate
Brave as a CDP observation browser silently skews tracker/analytics studies (Shields + GPC)
Viewed 0 times
remote-debugging-portGlobal Privacy ControlSec-GPCBrave Shieldstracking pixelmitmproxyuser-data-dirconsent banner
Error Messages
Problem
When you use Brave with --remote-debugging-port to observe how a third-party analytics or tracking script behaves (cookies, beacons, consent flow), the script often looks like it does nothing: the command queue array is never consumed, no tracking cookies appear and no beacon requests leave. There is no console error. Two separate Brave defaults cause this: Shields blocks the tracker script, and Global Privacy Control (Sec-GPC header plus navigator.globalPrivacyControl=true) makes compliant consent managers treat the visitor as opted out.
Solution
Use a dedicated --user-data-dir, never the main profile. (1) Turn Shields off for that profile only. With the browser closed, set profile.default_content_setting_values.braveShields = 2 (2 = BLOCK = shields down) in <profile>/Default/Preferences. Writing a fresh Preferences file that contains only that key also works for a new profile. (2) Turn GPC off with --disable-features=BraveGlobalPrivacyControl. Leave GPC on only when GPC handling is what you are studying. To check a tracker really ran: its queue's push is no longer Array.prototype.push, and its resource timing entry has a non-zero transferSize. Other useful tips: to MITM only this profile, pass --proxy-server=http://127.0.0.1:8080 and --ignore-certificate-errors-spki-list=<base64 sha256 of the proxy CA SPKI>, which needs no system keychain change. To reset state over CDP, call Storage.clearCookies on the browser target: Storage.clearDataForOrigin there returns "Internal error".
Why
Shields blocks requests to known tracker domains before they reach the network. The resource timing entry still exists but has transferSize 0, responseStatus 0 and a duration of about 1ms, so the request looks "loaded" at first glance. Brave also sends GPC by default, and many consent banners map it to a refused, non-persisted consent.
Gotchas
- Resource timing entries exist for Shields-blocked requests, so check transferSize and responseStatus, not just whether the entry is there
- Edit Preferences only while that profile's browser is closed, because Chromium rewrites the file on exit
- Chrome/Brave 136+ refuse remote debugging on the default user-data-dir anyway, so always use a separate one
Code Snippets
SPKI hash of the mitmproxy CA, for --ignore-certificate-errors-spki-list
openssl x509 -pubkey -noout -in ~/.mitmproxy/mitmproxy-ca-cert.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64Shields down by default in an isolated profile (browser must be closed)
import json, os
p = PROFILE + '/Default/Preferences'
d = json.load(open(p)) if os.path.exists(p) else {}
d.setdefault('profile', {}).setdefault('default_content_setting_values', {})['braveShields'] = 2
json.dump(d, open(p, 'w'), separators=(',', ':'))Revisions (0)
No revisions yet.