Browser Security is a local shield between you and the page for Chrome, Opera, and other Chromium builds: less tracking, a less unique fingerprint, and a clear warning on a shady address. There is no cloud: tab history stays in the browser, modules toggle independently, a site can be excluded, and the current tab can be paused.
- Tab and focus — a page should not reliably know that you switched away or minimized the window.
- Fingerprint — screen, Canvas, WebGL, language, and User-Agent look less unique.
- Network — requests to localhost and private addresses are blocked; WebRTC can be hardened.
- Trackers and isolation — a short tracker-domain pack, plus optional profile-wide Referer / ping / prefetch.
- Device — tighter page storage; camera and microphone are hidden, speakers stay listed.
- Suspicious sites — a local banner on plain HTTP, a phishing-like host, or a redirect chain.
- Nuisance — popups without a gesture, cosmetic junk, telemetry; separately, a copy-unlock helper.
- Controls — popup for quick actions, options page for everything else, per-domain stats, UI in ru / en / uk.
Chromium extension (Manifest V3): focus/visibility hardening, anti-fingerprinting, network and device privacy tools, on-page caution banners for heuristic hits, and a full options page (public/options.html; see options_page in manifest.json): excluded domains, every module, statistics, accessibility. The toolbar popup offers quick toggles and an «All settings…» link.
Version: 3.0.0 (see also "version" in manifest.json).
What's new in 3.0.0
- Settings channel (isolated → MAIN) — more reliable HMAC delivery: lazy
data-fb-kcapture, key attribute removed after Focus ACK (or a 5s timeout), capture-phase listeners,postMessagewith target"*"(opaque origins no longer silently drop messages), and MAIN acceptance ofworkerScriptUrlwithoutchrome.runtime.- Device Security — camera/mic without speakers —
enumerateDeviceshides onlyaudioinput/videoinput; speakers (audiooutput) stay listed.getUserMedia/getDisplayMediaandpermissions.queryuse native-shaped denials (NotAllowedError/state: "denied") instead of brandedSecurityErrornoise in site consoles/Sentry.- Page compatibility — lockdown descriptors use no-op setters (vendor bundles no longer crash on
getUserMedia = …); geolocation is not replaced withundefined; blocked IndexedDB returns an async request error instead of a syncthrow.- Anti-fingerprint — screen / navigator / UA-CH lockdown getters also use no-op setters so page assignments in strict mode do not throw
TypeError.
Source: https://github.com/ppvikentiy/security-browser-ex
License: MIT
- Blocks common “tab blurred / hidden” signals:
visibilitychange(vendor variants),blur/focus/focusin/focusout - Spoofs
document.hidden,visibilityState, and related fields so the page sees an always-active tab when enabled - Hooks
addEventListener/removeEventListener/dispatchEventfor those types and, for matching event kinds, inline handlers onwindow/document(e.g.onfocus/onblur/onvisibilitychangeonly while that event type remains blocked) addEventListener/removeEventListenerinterceptors do not forwardunload/beforeunloadto native (and swallowPermissions-Policyviolations) so pages withunload=()do not spam the console- Strict lifecycle mode — optional toggle in settings (
focusBlockingStrict, off by default): additionally blocksfreeze,resume,pagehide,pageshow, including related inline properties (window.onpageshow,window.onpagehide,document.onfreeze/document.onresumewhere supported). May break some SPA flows and bfcache restore behavior
- Popup — master switch plus three modules: Focus Blocker, anti-fingerprint (
securityEnabled), Network Security. Per-tab pause; quick-add the current host to exclusions. ADS Block, Device Security, Threat Shield, Privacy pack, and isolation are configured only on the options page - Anti-fingerprint (Security) — JS spoofing plus request headers: screen/window, battery, CPU/memory,
matchMedia, WebGL, canvas noise, timezone, navigator + UA + Client Hints, languages/Accept-Language, font allowlist; fingerprint modes (per domain / session / random) - Network Security — DNR blocks page-initiated requests to localhost/private/link-local targets; optional WebRTC IP-hardening via
chrome.privacy.network.webRTCIPHandlingPolicy - Privacy pack — separate DNR tracker list (narrow vs wide resource types); independent of ADS Block cosmetics/popups
- Isolation (
chrome.privacy) — optional profile-wide Referer off, hyperlink auditing off, network prediction off (see payment/SSO caveats in the UI) - Device Security — strict limits on
localStorage/sessionStorage, IndexedDB, Cache API; hide camera/microphone (speakersaudiooutputkept) and block geolocation; lockdown descriptors that do not TypeError on page assignment - Threat Shield (labeled «Активная интернет защита» in the Russian options UI) — top-frame overlay banner when heuristics match: plain HTTP on public hosts, builtin + custom suspicious host patterns, “stacked TLD” look-alikes, optional long/garbage-looking FQDN heuristic, chains of HTTP redirects before the document loads; whitelist and extra patterns in settings. Banner copy is localized (ru / en / uk) in
src/threat-shield.jsfrom the UI language - ADS Block (formerly DS Block; code —
src/ds-block.js; internal message keys still use theDS_BLOCKprefix) — popups without a user gesture, cosmetic CSS (viachrome.scriptingfor strict CSP), telemetry domain blocking via DNR; custom domain/selector lists - Copy helper — unlock common copy blocks; highlight + shortcuts (see the «Copy assistant» / «Помощник при копировании» section — wording follows UI locale). Script runs in the isolated world at
document_idle(separatecontent_scriptsentry, not MAIN) - Accessibility — UI language (ru / en / uk) for the options page, popup, and Threat Shield banner; reduced motion and calmer UI (Accessibility section). String catalogs live in
src/i18n.js - Excluded domains — host patterns (
*.example.comstyle); affects modules and some DNR rules; profile-widechrome.privacytoggles do not automatically follow per-site exclusions - Statistics — per top-level host counters: focus, FP spoofs, JS network, DNR, device, ADS Block; with
declarativeNetRequestFeedback, DNR matches feed the badge/table
Reload pages after changing settings.
- Download ZIP from https://github.com/ppvikentiy/security-browser-ex (Code → Download ZIP), or clone this repository
- Unzip if you used ZIP
- Open the extensions page: Chrome —
chrome://extensions/, Opera —opera://extensions/; enable Developer mode - Load unpacked → select the folder that contains
manifest.json - Options: click «All settings…» in the popup, or open the extension’s details page from the extensions list and launch Extension options (wording differs slightly by Chromium build)
- Browsers: Chromium 111+ — Google Chrome and compatible builds, including Opera
- Manifest: 3
- Sites:
http/https(manifest:match_about_blank,match_origin_as_fallback,all_frames)
node tools/check-world-copies.mjs— verify thatfb-channel.js/fb-channel-main.jsandsecurity-defaults.js/security-defaults-main.jsare byte-identicalnode tools/check-world-copies.mjs --fix— copy the primary files over the MAIN copiesnode tools/gen-i18n.mjs— rebuildsrc/i18n.jsfrom the catalogs inside the generator (after UI string changes)
How the extension talks to the browser and to itself—not a public HTTP API. This is an internal contract (Chrome Extension APIs, chrome.runtime.sendMessage, postMessage between page worlds).
| Permission | Role |
|---|---|
storage |
Options: chrome.storage.local is canonical (with a best-effort one-time migrate from sync when local is empty); session maps in chrome.storage.session (paused tabs, cosmetic CSS text, per-tab stat bucket) |
tabs, windows |
Popup state, badge, hostname for stats |
scripting |
Insert/remove ADS Block cosmetic CSS (insertCSS / removeCSS) |
declarativeNetRequest, declarativeNetRequestFeedback |
Dynamic block/modify-headers rules; optional onRuleMatchedDebug for DNR stats |
privacy |
WebRTC policy, Referer / ping / network prediction |
host_permissions *://*/* |
DNR conditions and normal pages |
- Isolated world,
document_start—fb-channel.js,security-defaults.js,settings-bridge.js,stats-bridge.js - MAIN,
document_start—fb-channel-main.js,security-defaults-main.js,stats-main.js,device-security.js,content.js,security.js,network-security.js,ds-block.js,threat-shield.js(single entry: the same path cannot be injected twice) - Isolated world,
document_idle—copy-helper.js
web_accessible_resources: src/security-worker.js for heavy anti-fingerprint patches.
On runtime.onInstalled, runtime.onStartup, and relevant storage changes, reloadFromStorageSnapshot() refreshes:
- UA + Client Hints headers —
modifyHeadersrule id990001when the extension, Security, and Navigator/UA flag are on; if Chromium rejects the full set, fallback is “full set → trimmed → User-Agent only” - Accept-Language — separate
modifyHeadersrule id990002when Security and Languages are on - LAN/loopback blocks — up to 12
blockrules withregexFilterfor localhost / RFC1918 / link-local / ULA IPv6; resource types omitmain_frame/sub_frameso top-level LAN pages still load (slots from990020) - ADS Block telemetry — chunked
requestDomainsrules, 40 domains per rule, slots from990060(18 slots) - Privacy pack — same pattern, slots from
990078(20 slots), narrow or wideresourceTypes - WebRTC —
chrome.privacy.network.webRTCIPHandlingPolicy.setorcleardepending on Network Security - Isolation —
referrersEnabled,hyperlinkAuditingEnabled,networkPredictionEnabledviachrome.privacy
Threat Shield storage keys participate in the same reload path when those options change.
Also:
- Tab pause —
chrome.storage.sessionkeyfocusBlockerPausedTabIds({ [tabId]: true }), cleared on URL change / tab close - ADS Block cosmetics — CSS per
tabId(focusBlockerDsCosmeticCssByTabId) applied via thescriptingAPI - Statistics — key
focusBlockerStatsByHostinchrome.storage.local(see sync migrate above); increments from content messages and fromdeclarativeNetRequest.onRuleMatchedDebugwhen available; counter fields:focus,fpSpoof,netJs,device,ds,dnrBlock,dnrModify - Badge — sum of the active tab’s bucket in
focusBlockerTabStat(session)
type field on the message body:
type |
From | Purpose |
|---|---|---|
FB_STATS_REPORT |
stats-bridge.js (isolated world) |
deltas, breakdown, topHost for stats and badge |
FB_DS_BLOCK_SET_COSMETIC_CSS |
settings-bridge.js, top frame |
Enable/disable injected hide CSS for the tab |
FB_IS_TAB_PAUSED |
settings-bridge.js, copy-helper.js |
Pause flag for current tab |
FB_REFRESH_SETTINGS |
background.js → tab |
Re-read storage/pause and rebroadcast settings to MAIN (settings-bridge.js, copy-helper.js) |
FB_POPUP_GET_STATE |
popup.js |
Host, flags, exclusions, injectable, paused |
FB_POPUP_SET_TAB_PAUSE |
popup.js |
Toggle pause for the active tab |
FB_POPUP_SET_STORAGE_BOOL |
popup.js |
Only keys extensionGloballyEnabled, focusBlockingEnabled, securityEnabled, networkSecurityEnabled |
FB_POPUP_ADD_HOST_EXCLUSION |
popup.js |
Append the current host to excludedDomains |
Responses are async (return true in the listener when sendResponse is used).
Isolated world reads chrome.storage, applies domain exclusions, the global master switch, and tab pause, then posts to the MAIN world via window.postMessage plus duplicate CustomEvents. Every message is HMAC-SHA256 signed (seq + sig fields, see Internal channel security); MAIN modules ignore unsigned or replayed messages:
type in postMessage |
Purpose |
|---|---|
FOCUS_BLOCKER_SETTINGS |
Focus module on/off + blocked event list for content.js |
FOCUS_BLOCKER_SECURITY_SETTINGS |
Security payload + worker URL for heavy patches |
FOCUS_BLOCKER_NETWORK_SETTINGS |
Network Security flags for network-security.js |
FOCUS_BLOCKER_DS_BLOCK_SETTINGS |
ADS Block payload (historic DS_BLOCK naming) |
FOCUS_BLOCKER_DEVICE_SECURITY_SETTINGS |
Device Security |
FOCUS_BLOCKER_THREAT_SHIELD_SETTINGS |
Threat Shield merged prefs + builtin host patterns and stacked-TLD tail list |
Resend request: FOCUS_BLOCKER_REQUEST_SETTINGS; ack: FOCUS_BLOCKER_SETTINGS_ACK.
The __focus_blocker_*_cache_v1 entries live in the page's own localStorage and are always treated as untrusted: they cannot be signed (the HMAC key is minted per page load) and a page can delete them regardless. Hence the rule — a cache may only ever raise protection above the fail-safe default, never relax it.
- Focus Blocker reads the
__focus_blocker_focus_cache_v1boot hint atdocument_startso focus/visibility events are already blocked before page scripts run. OnlyisEnabled: trueis consumed; the event list is always the module’s ownDEFAULT_BLOCKED_EVENTS. Cachedfalseand a cached event list are ignored. The bridge writes only the on/off flag, not the event list. - ADS Block reads
__focus_blocker_ds_block_cache_v1atdocument_startso thewindow.open/sendBeacon/ telemetry hooks are armed before page scripts run. Only boolean flags are consumed, and only whentrue; cachedfalsevalues, domain/selector lists andpageAllowedare ignored, and user lists are never written to the cache. - Threat Shield, Network Security and Device Security never read or write a cache:
isActiveis set only by a signed bridge message. Stale entries for those modules are deleted on the first settings broadcast. - The full anti-fingerprint config is never persisted to page
localStorage.
The isolated → MAIN channel is authenticated so a malicious page cannot disable modules or alter config with forged postMessage / CustomEvent traffic:
src/fb-channel.js(isolated world) andsrc/fb-channel-main.js(MAIN) — shared library, loaded first in itscontent_scriptsentry: pure-JS SHA-256 / HMAC-SHA256 (synchronous, works onhttp://pages wherecrypto.subtleis unavailable) plus a deterministicstableStringifywith sorted keys. All natives it relies on (TextEncoder,JSON.stringify,Object.keys,Array.prototype.sort,Uint8Array, …) are captured atdocument_startbefore any page script runs, and the exported API is frozen so the page cannot swap methods to steal the key. It is published in three places —globalThis.__fbChannel,document.documentElement.__fbChannelApi, andDocument.prototype.__fbChannelGet(all non-writable)- The byte-identical per-world copies are deliberate. A script path listed in several
content_scriptsentries can be injected into a document only once, so the isolated entry consumed the single injection and MAIN modules were left with no channel at all, rejecting every signed settings message (Threat Shield silent, Device Security stuck fail-closed with IndexedDB blocked). The defaults file is duplicated for the same reason —src/security-defaults.js(isolated world, options page, service worker) andsrc/security-defaults-main.js(MAIN) — otherwise MAIN modules silently fell back to trimmed inline defaults and ADS Block lost its builtin domain/selector lists. When you change one file, copy it over the other; verify or repair both pairs withnode tools/check-world-copies.mjs [--fix] - Key — 32 random bytes per page load; the bridge hands it to MAIN modules via a short-lived
<html data-fb-k="...">attribute. MAIN modules re-read the key on verify if needed; the attribute is removed after Focus ACK or a ~5s timeout, not immediately after the first async broadcast. The key is never placed inside messages - Signature — each message carries a monotonic
seqcounter andsig = HMAC(key, stableStringify(payload without sig)); MAIN modules verify the signature and require a strictly increasingseq(anti-replay) - Fail-closed — Network Security and Device Security start enabled and can only be turned off by a signed message; the anti-fingerprint
workerScriptUrlis accepted from the HMAC payload as achrome-extension://…/src/security-worker.jsURL (MAIN has nochrome.runtime); critical API hooks are pinned withconfigurable: falseplus a no-op setter so page assignment does not crash - The cosmetic-CSS handler in
background.jsadditionally checkssender.idand caps CSS size
Residual risk: the key attribute exists from document_start until ACK/timeout (usually milliseconds to a few seconds) — an inline script at the top of <head> could theoretically read it. The attack bar is raised from "passively listen to postMessage" to "actively read a DOM attribute within the load window".
MAIN posts FOCUS_BLOCKER_STATS_DELTA (deltas, breakdown, topHost) → isolated stats-bridge.js → FB_STATS_REPORT to the service worker.
src/fb-channel.js,src/fb-channel-main.js— settings-channel crypto primitives (HMAC-SHA256, canonical serialization); identical copies for the isolated and MAIN worldssrc/security-defaults.js,src/security-defaults-main.js— defaults, presets and settings-merge helpers; the same pair of copiessrc/settings-bridge.js,src/stats-bridge.js— isolated world: storage → MAIN, stats → backgroundsrc/content.js— Focus Blocker in MAINsrc/security.js,src/security-worker.js— anti-fingerprintsrc/network-security.js— fetch/XHR/WebSocket hooks (per settings)src/device-security.js— device APIs / storage capssrc/ds-block.js— ADS Block (popups, telemetry, cosmetics with background)src/copy-helper.js— copy UX (isolated,document_idle)src/threat-shield.js— Threat Shield banner/heuristics (banner in the top frame only; strings follow UI language)src/i18n.js— ru / en / uk catalogs for popup, options page, and banner (built bytools/gen-i18n.mjs)src/options.js,public/options.html— options pagesrc/popup.js,public/popup.html— popupsrc/background.js— service worker
MIT. Details are in the LICENSE file.




