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.
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.
Config sync and script injection (background.js + contentScript.js)
async 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; }
}function 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 load
chrome.storage.local.get(['opts']).then(store => {
if (store.opts) processOpts(store);
});- adblockerfortify.com
Developer-controlled server; issues per-install tokens via /new and delivers per-install site injection config via /opts/?token=<opt>.
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.
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.
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]
});
}- adblockerfortify.com
Developer-controlled server that issues per-install tokens and delivers JSON rule sets applied directly to Chrome's declarativeNetRequest engine.
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.
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.
| Field | Value | Why it matters | |
|---|---|---|---|
Your installation token | ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR0I3bldwa2YwK2tCWkY4c0R3SjhOTnp4Mnd4R3dMdUFjakt1ckNlYnpn | A unique value assigned to your installation. It doesn't change between sessions or updates; every future request carries it. | |
Request timestamp | 2026-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 address | 93.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 token assignment and attachment logic in background.js
// On first install, request a unique token from the server
const 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();
});// Reads the stored token and appends it to any URL passed in
async 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;
}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() });
}The per-install token, written on first install, is never cleared; every future request includes it. Two test installs got different tokens.
chrome.storage.local key 'opt'ai9WZkg2SXJUU20veDY0QTU5UmhKeUhYM0p2UU1NR0I3bldwa2YwK2tCWkY4c0R3SjhOTnp4Mnd4R3dMdUFjakt1ckNlYnpn
- 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.