Is Adblocker Fortify safe?
Adblocker Fortify is critical risk. Adblocker Fortify fetches a config object listing which sites get extra injected scripts. The operator can add or remove target sites anytime, with no visible update. The injected script, block.js, runs inside every targeted page.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Server controls which websites receive injected scripts via remote config
Adblocker Fortify fetches a config object listing which sites get extra injected scripts.
The operator can add or remove target sites anytime, with no visible update.
The injected script, block.js, runs inside every targeted page.
What actually happens
The content script loads on every page visit.
contentScript.js runs at document_start on <all_urls> (manifest content_scripts declaration).
The script reads a server-provided configuration and, when the current site matches an operator-specified entry, injects block.js into the page.
function c() creates a script element, sets its src to chrome.runtime.getURL(opts.opts[i][19]), and appends it to document.head. Target sites are specified in opts.opts[i][21], received from the remote config endpoint.
The code that does this
Config sync and script injection (background.js + contentScript.js)
prefManager.sync() — config fetch
background.jsasync sync(token) { try { // Fetch: https://adblockerfortify.com/opts/?token=<opt> const data = await fetch(u.opts + token).then(r => r.json()); // Store full response object as 'opts' in chrome.storage.local await chrome.storage.local.set({ opts: data }); } catch (e) { throw e; }}contentScript.js — per-page injection loop
contentScript.jsfunction injectScriptForConfig(configArray, index, configRoot) { // Create element of server-specified type (e.g. 'script') let el = document.createElement(configArray[index][18]); // Set src to extension's block.js URL el.src = chrome.runtime.getURL(configArray[index][19]); document.head.appendChild(el);}function processOpts(opts) { const targets = opts.opts[7]; // server-specified index list if (!Array.isArray(targets)) return; targets.forEach(index => { const targetSites = opts.opts[index][21]; // hostname(s) for this entry if (currentHostnameMatches(targetSites)) { injectScriptForConfig(opts.opts, index, opts); } });}// Runs on every page loadchrome.storage.local.get(['opts']).then(store => { if (store.opts) processOpts(store);});Remote configuration source
- adblockerfortify.com
Developer-controlled server; issues per-install tokens via /new and delivers per-install site injection config via /opts/?token=<opt>.
What block.js does on injected pages
block.js contains an element-picker overlay (for user-triggered ad removal) and reads additional configuration from its own script element's data attributes. The operator's ability to control which sites receive this script via server config means the injection target list can change at any time without a CWS update.
Ad-blocker fetches and applies network rules from remote server every hour
Adblocker Fortify fetches rules from the developer's server every 60 minutes via a per-install token.
The response applies to Chrome's blocking engine, unvalidated.
The operator can add allow-rules bypassing blocking, without any update.
What actually happens
A Chrome alarm fires 60 minutes after install and every 60 minutes thereafter.
The alarm named 'updateNetRulesAlarm' is registered during installation in NetworkManager.setupUpdateAlarm().
The extension downloads a rule set from the developer's server and replaces all of Chrome's dynamic blocking rules with the server's response.
chrome.declarativeNetRequest.updateDynamicRules() removes all current dynamic rules and applies the server-provided array without any local schema check or size limit.
Captured request
JSON array of declarativeNetRequest rule objects; content varies per server response.
The code that does this
Rule fetch and apply loop (background.js lines 38-66)
async updateNetworkData() { const { lastUpdateTimestamp: e } = await chrome.storage.local.get({ lastUpdateTimestamp: 0 }); if (!(e && Date.now() - e < this.ms)) { // Build URL: https://adblockerfortify.com/opts/all/?token=<opt> const url = await this.addNetworkParams(this.durl + '/opts/all/'); const response = await fetch(url); const rules = await response.json(); // Replace ALL dynamic rules with server-supplied set await this.updateNetworkItem(rules); }}async updateNetworkItem(e) { // ... const existing = await chrome.declarativeNetRequest.getDynamicRules(); const removeIds = existing.map(r => r.id); await chrome.declarativeNetRequest.updateDynamicRules({ removeRuleIds: removeIds, addRules: [...serverRules, ...preservedHighPriorityRules] });}Remote rule source
- adblockerfortify.com
Developer-controlled server that issues per-install tokens and delivers JSON rule sets applied directly to Chrome's declarativeNetRequest engine.
When this fires
Every 1 hour
Rule update fires every 60 minutes after install via a Chrome alarm named 'updateNetRulesAlarm'.
Per-install tracking token sent on every config and rule update
On install, Adblocker Fortify gets a unique token from adblockerfortify.com, stored permanently.
Every later request there includes it as a URL parameter.
DA on two installs found different, per-install tokens persisting across restarts.
What actually happens
You install Adblocker Fortify.
The extension fetches a unique token from adblockerfortify.com and stores it permanently.
From that point on, every request the extension makes to its update server includes this token as a query parameter, allowing the server to track that installation across all future update checks.
Captured request
200 OK, returns JSON array of declarativeNetRequest rules applied as dynamic ad-blocking rules
What the token request reveals to the server
- Your installation tokenai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR0I3bldwa2YwK2tCWkY4c0R3SjhOTnp4Mnd4R3dMdUFjakt1ckNlYnpn
A unique value assigned to your installation. It doesn't change between sessions or updates; every future request carries it.
- Request timestamp2026-04-18T02:47:55Z
The server receives each update check as it happens, building a timeline of when your browser was active with the extension running.
- Your IP address93.184.216.34
Each request exposes the IP your browser is using then. Combined with the token, this links your IP history to your install.
The code that does this
The token assignment and attachment logic in background.js
Token fetch on install
// On first install, request a unique token from the serverconst response = await fetch('https://adblockerfortify.com/new');const data = await response.json();chrome.storage.local.set({ opt: data.opt, // store the unique token range: data.range ?? 7200}, () => { prefManager.schedule(data.range ?? 7200); // token is also embedded in the uninstall URL so the server // knows when this specific installation is removed chrome.runtime.setUninstallURL(data.del + data.opt); updateNetworkData();});Token appended to all update requests
// Reads the stored token and appends it to any URL passed inasync function addNetworkParams(url) { return url + '?token=' + await getCachedToken();}async function getCachedToken() { // In-memory cache so storage isn't hit on every request if (!this.tokenCache) { const result = await chrome.storage.local.get(['opt']); this.tokenCache = result.opt; } return this.tokenCache;}Periodic rule update (fires every 60 minutes)
async function updateNetworkData() { const { lastUpdateTimestamp } = await chrome.storage.local.get({ lastUpdateTimestamp: 0 }); // Skip if updated within the last 24 hours (this.ms = 864_000_000 ms) if (lastUpdateTimestamp && Date.now() - lastUpdateTimestamp < this.ms) return; // Token is appended here — every rule fetch goes out with ?token=<opt> const urlWithToken = await this.addNetworkParams('https://adblockerfortify.com/opts/all/'); const response = await fetch(urlWithToken); const rules = await response.json(); await this.updateNetworkItem(rules); await chrome.storage.local.set({ lastUpdateTimestamp: Date.now() });}What's stored on your device
The per-install token, written on first install, is never cleared; every future request includes it. Two test installs got different tokens.
- Location
- chrome.storage.local key 'opt'
ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR0I3bldwa2YwK2tCWkY4c0R3SjhOTnp4Mnd4R3dMdUFjakt1ckNlYnpnWhere the token is sent
- adblockerfortify.com
The extension's update server. Receives the token on every rule/config request, issues it on install, and gets an uninstall notice via setUninstallURL.
What it can do
Permissions this extension asks for, as declared in version 1.0.4. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on every site you visit
<all_urls>
Block and redirect the requests your browser makes
declarativeNetRequest
See which of your requests its blocking rules matched
declarativeNetRequestFeedback
Store data in your browser
storage
Act on the current tab, but only after you click the extension
activeTab
See the address and title of every tab you have open
tabs
Schedule its own background tasks
alarms
Store an unlimited amount of data in your browser
unlimitedStorage
Watch every request your browser makes
webRequest