Is Stop Ads Now safe?

High risk

Stop Ads Now collects hardware fingerprints and visited domain history from every page and sends them to stopads-now.com.

On every page load across all sites, the extension gathers a hardware fingerprint — including canvas data, GPU identifiers, CPU and memory figures, battery status, and screen resolution — and transmits it to stopads-now.com/stopads.php along with a running list of up to 50 visited domains. At install time it also captures the hostnames of all open tabs and includes them in the first sync. Additionally, the backend can push new network-blocking rules at any time, dynamically controlling which URLs the browser blocks or allows for all users.

Stop Adsv1.7Chrome Web Store
79Risk
Who publishes it

Stop Ads - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Stop Ads

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Visited domain history periodically transmitted to stopads-now.com

DA captured POSTs to stopads-now.com/stopads.php with every domain visited plus visit counts.

The content script runs on every load, records the domain, then a sync sends the history, sent within seconds of navigation.

Severity
High unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You navigate to any website.

The content script is injected at document_start on every top-level page load across all URLs.

The extension did this

The extension records the domain in local storage and, 5 seconds later, includes your full visited-domain history in a POST to stopads-now.com.

This happens on every navigation, not just on install. Each sync carries the running tally of domains and visit counts.

Captured request
POSThttps://stopads-now.com/stopads.php

Server responds with updated config including a rules array. Body ~3.9 MB.

Headers
Content-Type
application/json
Body
{  "core_visited_sites": {    "en.wikipedia.org": 1  },  "coredetails": "eyJhbGciOiJIUzI1NiJ9...",  "extid": "caeeihoelgmdmafbmlgoebfjnmgpfend",  "core_last_sync": 1747569244261}
What's stored on your device

A rolling map of up to 50 visited domains with visit counts each, included verbatim in every POST to stopads-now.com after each navigation.

Location
chrome.storage.local key 'core_visited_sites'
Contents
{  "github.com": 7,  "mail.google.com": 12,  "en.wikipedia.org": 3,  "stackoverflow.com": 5,  "news.ycombinator.com": 2}
The code that does this

Domain history tracking, content/broker.js lines 195-245

Readable version
(async function recordVisitedDomain() {  const isTop = (() => { try { return window.top === window; } catch { return true; } })();  if (!isTop) return;  const domain = normHost(location.hostname);  if (!domain) return;  try {    const result = await chrome.storage.local.get(["core_visited_sites"]);    let data = result?.core_visited_sites;    if (!data || typeof data !== "object" || Array.isArray(data)) data = {};    if (Object.prototype.hasOwnProperty.call(data, domain)) {      data[domain] = (Number(data[domain]) || 0) + 1;    } else {      data[domain] = 1;      const keys = Object.keys(data);      if (keys.length > 50) {        const sorted = Object.entries(data).sort(([, a], [, b]) => (Number(a) || 0) - (Number(b) || 0));        const toDelete = Math.min(10, sorted.length);        for (let i = 0; i < toDelete; i++) delete data[sorted[i][0]];      }    }    await chrome.storage.local.set({ core_visited_sites: data });    setTimeout(async () => {      try {        await chrome.runtime.sendMessage({ eventName: "CHECK_AND_FETCH_DATA", params: { domain } });      } catch {}    }, 5000);  } catch {}})();
Destination for browsing history
    • stopads-now.com

    Receives the visited-domain history map each navigation. Combined with the fingerprint identity set at install, builds a persistent profile. Not disclosed in the listing.

Hardware fingerprint collected on every page and sent to stopads-now.com

DA captured a POST to stopads-now.com/stopads.php with a hardware fingerprint: GPU, canvas output, CPU cores, RAM, battery, resolution, timezone, language.

Fires on first load, before consent.

Fingerprints survive clearing cookies.

Severity
High unwanted
Type
Unexpected
CWE
CWE-359
Source
Dynamic sandbox
What actually happens
You did this

You install the extension and navigate to any webpage.

The content script runs at document_start on the very first top-frame page load.

The extension did this

The extension renders a off-screen canvas, queries your GPU, reads CPU and RAM counts, and sends the full fingerprint to stopads-now.com.

This runs once, only when no coredetails token is stored, but the fingerprint creates a persistent identity that cannot be cleared by deleting cookies.

Captured request
POSThttps://stopads-now.com/stopads.php

Server responds with a coredetails token and a rules array for declarativeNetRequest. Body ~3.9 MB.

Headers
Content-Type
application/json
Body
{  "cpu_cores": 12,  "ram_gb": 32,  "gpu_vendor": "Google Inc. (Google)",  "gpu_renderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)))",  "canvas_data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAAyCAYAAAAZUl3oAAAAAXNSR...",  "lang": "en-GB",  "color_depth": 24,  "timezone": "UTC",  "screen_res": "1920x1080",  "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",  "touch_points": 0,  "bat_charging": 1,  "bat_level": 0.8,  "an": null,  "cid": null,  "sid": null,  "extid": "caeeihoelgmdmafbmlgoebfjnmgpfend"}
Fields included in the hardware fingerprint POST
  • Canvas rendering output
    data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAAy...

    A PNG snapshot of text rendered in an off-screen canvas. The pattern is unique to your GPU and fonts, surviving cleared cookies or a VPN.

  • GPU vendor and renderer
    ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device))

    Your graphics card make and driver string. Combined with canvas output, this narrows device identity to a small fingerprint space.

  • CPU core count
    12

    Number of logical CPU cores reported by your browser.

  • RAM size
    32

    Device memory in GB as reported by the browser.

  • Battery state
    charging: true, level: 0.8

    Whether your device is charging and current battery level, narrows device type on mobile.

  • Screen resolution and color depth
    1920x1080, 24-bit

    Physical screen dimensions and color bit depth.

  • Timezone and language
    UTC, en-GB

    Your local timezone identifier and browser language setting.

The code that does this

Fingerprint collection, content/broker.js

Readable version
(async function collectFingerprint() {  const isTop = (() => { try { return window.top === window; } catch { return true; } })();  if (!isTop) return;  try {    const stored = await chrome.storage.local.get(["coredetails"]);    if (stored.coredetails && stored.coredetails !== "") { console.log("❌ FP ok, skipping"); return; }    const vars = await chrome.storage.local.get(["an", "cid", "sid"]);    const fpData = await gatherFingerprint(vars);    chrome.runtime.sendMessage({ type: "CORE_FINGERPRINT_READY", payload: fpData });  } catch {}})();async function gatherFingerprint(vars) {  const gpu = getGPUDetails();  let batCharging = "unknown"; let batLevel = "unknown";  try { if ("getBattery" in navigator) { const battery = await navigator.getBattery(); batCharging = battery.charging ? 1 : 0; batLevel = battery.level; } } catch {}  return {    cpu_cores: navigator.hardwareConcurrency || "unknown",    ram_gb: navigator.deviceMemory || "unknown",    gpu_vendor: gpu.vendor, gpu_renderer: gpu.renderer,    lang: navigator.language || "unknown",    color_depth: screen.colorDepth || "unknown",    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "unknown",    canvas_data: getCanvasFingerprint(),    touch_points: navigator.maxTouchPoints || 0,    an: vars.an || null, cid: vars.cid || null, sid: vars.sid || null,    screen_res: `${screen.width}x${screen.height}`,    user_agent: navigator.userAgent,    bat_charging: batCharging, bat_level: batLevel,  };}
Destination for fingerprint data
    • stopads-now.com

    Receives the fingerprint POST, returns an identity token (coredetails); also delivers DNR rules and history sync. Not disclosed in the Chrome Web Store listing.

Remote server controls which URLs Chrome blocks or allows via pushed rules

DA captured the server response to stopads.php returning 33 declarativeNetRequest rules, applied verbatim with no validation.

One rule redirected http(s) requests matching 4,600+ domains to the blocked page, server-controlled.

Severity
High unwanted
Type
Unexpected
CWE
CWE-829
Source
Dynamic sandbox
What actually happens
You did this

The remote server includes a rules array in its response to the extension's sync POST.

This occurs on every fetchData() cycle, at startup and 5 seconds after each page navigation.

The extension did this

The extension replaces all of Chrome's dynamic declarativeNetRequest rules with whatever the server provided, without validating the rule contents.

Any valid DNR rule object, including redirects, blocks, header modifications, or URL rewrites, is applied immediately.

Captured request
POSThttps://stopads-now.com/stopads.php

3.9 MB JSON response. Contains rules array at offset ~1.5 MB with 33 declarativeNetRequest rule objects. Rule id=1002 redirects http(s) URLs with requestDomains matching 4,637 entries to the extension's own blocked.html page.

Headers
Content-Type
application/json
Body
{  "coredetails": "eyJhbGciOiJIUzI1NiJ9...",  "extid": "caeeihoelgmdmafbmlgoebfjnmgpfend",  "core_last_sync": 1747569244261}
The code that does this

Server-controlled rule application, background.js lines 337-347

Readable version
async function updateDnrRules() {  const rules = await storage.get("rules");  if (!rules || !Array.isArray(rules)) return;  const existing = await dnr.getDynamicRules();  await dnr.updateDynamicRules({    removeRuleIds: existing.map((r) => r.id),    addRules: rules,  });  log("Currently applied DNR rules:", await dnr.getDynamicRules());}
Example server-supplied rule (id=1002) observed in dynamic analysis
  • Rule action type
    redirect

    Redirect; the browser sends the user to a different page instead of the original destination.

  • Redirect target
    chrome-extension://caeeihoelgmdmafbmlgoebfjnmgpfend/pages/blocked/blocked.html?url=\0

    Requests matching this rule are sent to the extension's own blocked page, which the extension controls entirely.

  • URL pattern matched
    ^http.+

    Any http or https URL; the rule is not limited to known ad domains.

  • Domains in requestDomains list
    4,637

    Number of domains in this single rule's allow-list at time of capture. The server can update this list on any sync cycle.

Server delivering dynamic browser rules
    • stopads-now.com

    Delivers the DNR rule set applied on every sync. Same endpoint receives hardware fingerprints and browsing history. Not disclosed in the Chrome Web Store listing.

+1 more finding not shown

Where it sends data

Destinations our analysis observed Stop Ads Now contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • stopads-now.com

    Stop Ads Now sends data to stopads-now.com. No other extension we have analysed sends data here.

Updated 30 September 2026caeeihoelgmdmafbmlgoebfjnmgpfend