Back to glossary

Browser Extension Security

Browser extension security is controlling what Chrome, Firefox, and Edge extensions can access and verifying what each auto-updated version actually does.

Also known as Chrome extension security, Chrome extension malware, malicious browser extensions, Firefox add-on security, Edge extension security

Browser extension security (also called Chrome extension security) is the practice of controlling what extensions can access and verifying what their code does. An extension runs inside the browser with permissions a user granted once and updates itself in the background, so a trusted extension can turn into Chrome extension malware in a later version the user never reviewed.

The most important fact in browser extension security is that the update channel, not the install, is where trust breaks. Chrome checks installed extensions for updates every few hours and installs new versions without asking. Google's permission warning guidelines describe the one exception: when an update adds a permission that carries a warning, "the extension will be disabled until the user accepts the new permission." Firefox behaves the same way, prompting again only when an update adds permissions. An update that stays inside permissions the user already granted reaches every install silently.

Our position: a permission list is a ceiling on what an extension can do, not a description of what it does. Judge an extension by the code in its current version, and judge it again on every update. The store listing, install count, rating, and badge describe a past version, published by whoever owned the listing at the time.

How does a browser extension get access to your data?

#

A browser extension gets access to your data through two fields in its manifest.json: permissions, which unlock browser APIs, and host_permissions, which unlock websites. Chrome, Edge, and Firefox share this format. Firefox moved to the common WebExtensions model in Firefox 57 in 2017.

An extension has three kinds of code. The background service worker runs in the extension's own origin and calls privileged APIs such as chrome.proxy or chrome.cookies. Content scripts are injected into every page that matches a pattern, and they can read and rewrite that page's DOM. Popups and options pages are the UI. Content scripts run in an isolated world: they share the page's DOM but not its JavaScript heap.

That power is the product. Ad blockers, password managers, translators, and accessibility tools all need to read and rewrite pages they did not write. The browser cannot tell a password manager filling a login form from an infostealer reading it, because both make the same API calls. Chrome's answer is consent: show a warning at install time and trust the user's decision.

Here is the manifest change that turned the "VPN Go: Free VPN" Chrome extension malicious in version 1.1, as published in our analysis of the VPN Go clipboard stealer:

JSON
{
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "run_at": "document_start",
      "js": ["scripts/version.js"]
    }
  ],
  "permissions": [
    "proxy",
    "storage",
    "webRequest",
    "clipboardRead",
    "webRequestAuthProvider",
    "alarms"
  ],
  "host_permissions": ["<all_urls>"]
}

Every line is a legitimate capability for some extension. <all_urls> with document_start runs scripts/version.js on every site before the page's own scripts load. clipboardRead lets that script call navigator.clipboard.readText(). Chrome's permissions reference maps clipboardRead to the warning "Read data you copy and paste" and proxy to "Read and change all your data on all websites." Nothing in the manifest says the script polls the clipboard every 500 milliseconds and ships each new value to a hardcoded IP address over HTTP. Only the code says that.

Are browser extensions safe?

#

Most browser extensions do what they claim, but an extension is only as safe as its current version and its current owner. The user makes one decision, at install time, about a version that may be replaced within hours. Figure 1 shows where each check sits.

  1. Install

    Chrome shows one warning per risky permission in the manifest. The user accepts once.

    Does not checkWhat the code does with the access. A proxy that reroutes traffic and a proxy that logs it show the same warning.

  2. Update check

    Every few hours. The new package must be signed with the same key.

    Does not checkWho holds the key. A buyer of the extension inherits it, the users, and the update channel.

  3. Permission diff

    New warning: extension disabled until the user acceptsNo new warning: installed silently

    Does not checkNew behavior inside permissions the user already granted.

  4. Runtime

    Remote config, server-side redirects, and C2-delivered modules stored in chrome.storage

    Nothing checksBehavior that changes with no update at all.

Figure 1. The user is asked once. After that, only a new permission warning stops an update.

Figure 1 in text: Chrome checks an extension at four points. At install, it shows a warning for each risky permission, and the user accepts once; this does not check what the code does with the access. Every few hours, it checks for an update that must be signed with the same key; this does not check who holds that key. When an update arrives, Chrome compares its permissions with those already granted: a new warning disables the extension until the user accepts, and no new warning means a silent install; this does not check new behavior inside existing permissions. At runtime, remote configuration, server-side redirects, and C2-delivered modules change behavior with no update at all, and nothing checks them.

VPN Go shows the update path in practice. Chrome version 1.0, published December 22, 2025, behaved like a proxy extension. Version 1.1, published May 31, 2026, added the clipboard stealer. Version 1.3, updated June 14, 2026, kept the stealer and moved to new infrastructure. The Firefox build added the same stealer in version 1.3.3 and polled the clipboard every 1.5 seconds. Under Chrome's rules, adding clipboardRead disables the extension until the user accepts a new warning. That prompt is the whole defense, and it asks the user to approve a capability, not to review code.

Ownership changes are invisible. In our Microsoft Edge extension research, we noted that users get no notification when a developer sells an extension. The buyer inherits the users, the granted permissions, and the update channel. Of the 19 Chrome and Edge extensions delivering a wallet drainer, the threat actor created 14 and bought 5 from legitimate authors. One purchased extension, "Enable Right Click & Copy — Smart Unlock + OCR," had around 70,000 Chrome users when the malicious functionality appeared.

Clean first, malicious later is a pattern, not an exception. In the 77 Firefox extensions linked to wallet and credential theft, nine confirmed malicious identities had first shipped as harmless sports-score shells. This is the same supply chain attack we see on npm and PyPI: compromise the distribution channel and let automatic updates deliver the payload. A vulnerability scanner finds nothing here, because malicious extensions do not get CVEs.

Which extension permissions matter most?

#

The extension permissions that matter most grant access to every site, run code in every page, read secrets, or reroute traffic. The table maps each to the warning Chrome shows and to where it appeared in Socket research. Warning text comes from Chrome's permissions reference.

Manifest entryWhat it grantsChrome warningWhere we saw it
host_permissions: ["<all_urls>"] or "*://*/*"Read and modify every page; API access to every site"Read and change all your data on all websites"VPN Go 1.1; Socket's Wildcard Host Permission alert is rated High
content_scripts with matches ["<all_urls>"] and run_at document_startCode in every page, before the page's own scriptsCovered by the all-sites host warningVPN Go's clipboard poller, scripts/version.js
clipboardReadnavigator.clipboard.readText()"Read data you copy and paste"VPN Go polled every 500 ms on Chrome, every 1.5 s on Firefox
proxyRoute all browser traffic through a chosen server"Read and change all your data on all websites"VPN Go's working proxy, which gave users a reason to keep it
declarativeNetRequestRules that block or rewrite requests and headers"Block content on any page"19 Chrome and Edge extensions stripped CSP headers on every page
tabsURLs and titles of open tabs"Read your browsing history" (hidden if <all_urls> is also requested)A Firefox remote-loader cluster requested only storage and tabs
scriptingchrome.scripting.executeScript at runtimeNone listedNewer builds of the wallet-drainer framework inject dynamically
storagePersistent key-value storageNone listedThe same framework stores encrypted C2 modules
chrome_settings_overrides.search_providerReplace the default search engineNot a permission; one prompt at install319 Searchjacked extensions, about 13.4 million users

Read the bottom rows first. storage and scripting show no warning, yet together they form the loader in our wallet-drainer research. The tabs warning does not even appear when an extension also requests <all_urls>, according to Google's permission warning guidelines. One remote-loader cluster in the 77-extension Firefox campaign requested only storage and tabs.

The last row needs no code at all. In our Searchjacked investigation, we tracked 319 extensions, installed by roughly 13.4 million users, that capture search queries by replacing the default search engine. Apart from three icon files, this manifest is the entire extension:

JSON
{
  "update_url": "https://clients2.google.com/service/update2/crx",
  "manifest_version": 3,
  "version": "1.1",
  "name": "Safe Game Search",
  "short_name": "Game Safe Search",
  "description": "Real-time safety alerts for game searches and risky game websites",
  "default_locale": "en",
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "chrome_settings_overrides": {
    "search_provider": {
      "name": "Safe Game",
      "keyword": "Safe Game Search",
      "is_default": true,
      "encoding": "UTF-8",
      "favicon_url": "https://safesearchgame.com/favicon.ico",
      "search_url": "https://search.safesearchgame.com/query?q={searchTerms}",
      "suggest_url": "https://suggest.safesearchgame.com/querying/autocomplete?query={searchTerms}"
    }
  }
}

There are no permissions, no service worker, no content scripts, and no UI. Every query goes to search.safesearchgame.com before it reaches a real search engine. The operator's proxy appends a Yahoo or Bing partner ID to earn referral revenue and chooses the destination per request. For one extension, Predator Search, six identical searches ended in four different places. A permission review scores this extension as harmless, which is exactly the mistake we argue against in that report.

How malicious extensions get past store review

#

Store review evaluates a submission at a point in time, and malicious extensions are designed around that. Our research shows four routes:

  • Publish a clean version, build an install base, then ship the payload in an update.
  • Buy an established extension and push an update to users who already trust it.
  • Ship no code at all, as the search hijackers do, and move the behavior to a server.
  • Ship a loader, and deliver the payload after review over a command and control (C2) channel.

The fourth route matters because Manifest V3 was supposed to close it. Google's Manifest V3 security changes say extensions "can no longer execute external logic" with executeScript(), eval(), or new Function(). The same page says "You can still load data remotely at runtime." The ban covers code the extension runs as itself. It does not cover JavaScript that an extension stores as data and then hands to a web page to run.

C2 server

  1. Holds JavaScript modules: wallet drainer, seed-phrase phishing, form grabber
  2. Pushes modules over a WebSocket with a 5-minute heartbeat
  3. Can rotate the victim to a new C2 endpoint

Extension service worker

  1. Encrypts modules with AES-GCM and saves them under the nodes key in chrome.storage
  2. Registers a declarativeNetRequest rule that strips Content-Security-Policy from every page and frame

Visited page, main world

  1. Content script adds a hidden <img>, <input>, or <form> with the module as its event handler
  2. Fires the event; the module runs with the page's own privileges
  3. Removes the element

What the store received: a WebSocket client, a storage write, a header rule, and DOM code. The payload is in none of the shipped versions.

Figure 2. The loader framework in 19 Chrome and Edge extensions, reconstructed from Socket's analysis.

Figure 2 in text: the loader framework in the 19 Chrome and Edge extensions works in eight steps. The C2 server holds JavaScript modules, including a wallet drainer, seed-phrase phishing, and a form grabber. It pushes them over a WebSocket with a 5-minute heartbeat, and it can rotate a victim to a new C2 endpoint. The extension's service worker encrypts each module with AES-GCM and stores it under the nodes key in chrome.storage. It registers a declarativeNetRequest rule that strips the Content-Security-Policy header from every page and frame. On a visited page, a content script adds a hidden <img>, <input>, or <form> element with the module as its event handler. It fires the event, so the module runs in the page's main world, and then removes the element. The store received a WebSocket client, a storage write, a header rule, and DOM code. The payload was in none of the shipped versions.

Stripping the Content Security Policy (CSP) is what makes the injection work, because CSP is the page's own defense against injected script. Our analysis found 16 modules in the C2 traffic of one extension. They included a multi-chain wallet drainer, fake Ledger and Trezor recovery wizards, exchange session harvesters, a universal form grabber, browser-history exfiltration, and a fake update that told victims to run an attacker-supplied command.

How do you check if a browser extension is safe?

#

You check if a browser extension is safe by comparing what its code does with what its stated purpose needs, and repeating that check on every update. For an individual, the practical version is short. Remove extensions you do not use. For the rest, ask whether the purpose explains each warning: a VPN needs proxy, but it has no reason to read your clipboard. Chrome's runtime host permissions let users withhold site access and grant it per site or on click, which applies the least privilege principle to extensions that only need one site.

For an organization, the floor is policy. Chrome's ExtensionSettings policy can block everything by default, allow named extension IDs, refuse extensions that request specific APIs, and keep extensions away from sensitive sites:

JSON
{
  "*": {
    "installation_mode": "blocked",
    "blocked_install_message": "Request extensions through IT."
  },
  "abcdefghijklmnopabcdefghijklmnop": {
    "installation_mode": "allowed",
    "blocked_permissions": ["clipboardRead", "debugger", "proxy"],
    "runtime_blocked_hosts": ["*://*.okta.com", "*://console.aws.amazon.com"]
  }
}

The 32-character key stands in for the ID of each approved extension. Settings under "*" apply only to extensions without their own entry, so every approved ID carries its own blocks. Listing blocked_permissions on an approved ID works as a tripwire: an installed extension that requests a blocked permission does not load, so a future update that adds clipboardRead disables the extension instead of shipping silently.

Each control has a cost. runtime_blocked_hosts accepts at most 100 entries and discards the rest. Blocking a permission also blocks every legitimate tool that needs it. Most importantly, policy decides which extension IDs may run, not which versions. An approved ID receives every future update that stays inside its allowed permissions, including a malicious one. minimum_version_required sets a floor, not a pin. Edge adds a gap of its own: Edge users can install from both the Chrome Web Store and Microsoft Edge Add-ons, where similar tools ship under different extension IDs.

ControlWhat it catchesWhat it misses
Permission prompt at install and updateNew capabilitiesNew behavior inside granted permissions, remote modules, manifest-only hijackers
Store reviewKnown-bad code at submissionLater updates, purchased listings, payloads fetched after review
Allowlist (ExtensionSettings installation_mode)Extensions IT never approvedMalicious updates to approved extension IDs
blocked_permissions and runtime_blocked_hostsNamed high-risk APIs; access to up to 100 sensitive hostsEverything not on the lists
Per-version code and behavior analysisChanges in code, permissions, and endpoints between versionsPayloads that never ship in any version, unless the loader is detected

Verdict: use an allowlist as the floor and per-version code analysis as the gate on updates. Treat the permission prompt and store review as inputs, not security boundaries.

That gate is what Socket's browser extension coverage does. Socket analyzes extensions from the Chrome Web Store, Firefox Add-ons, and Microsoft Edge Add-ons, then compares each release with the previous one under the same extension ID for changes in code, permissions, and network endpoints. When we added Firefox in August 2026, Mozilla's public API listed 97,100 Firefox-compatible extensions; Edge Add-ons had more than 32,000 active extensions when we added Edge. Manifest-level findings surface as alerts such as Chrome: Wildcard Host Permission, rated High. As of October 2026, Firefox and Edge coverage is available in Experimental to enterprise customers. Version analysis has a limit too: a payload that only ever exists on a C2 server is caught by detecting the loader, not the payload.

Takedowns do not cross store boundaries

#

A takedown in one extension store does not remove the same code from another. When the Chrome Web Store removed "Enable Right Click & Copy," its Edge counterpart, with around 10,000 users, stayed live. The attackers then pushed an Edge update with a new C2 domain on August 14, 2026. Our Microsoft Edge extension research treats each store as a separate distribution channel for that reason.

Cross-store extensions also expose credentials that are not passwords. The JeetBot Twitch extension had about 30,000 Chrome users and 552 Firefox users when we reported it. It forwarded each viewer's Twitch OAuth token as an &auth= query parameter on a redirect to the operator's proxy. A bearer token needs no password and no second factor. The developer shipped fixes after publication, Firefox 85.8.7 by September 16, 2026 and Chrome 85.8.9 by September 25, but any install still running older code kept forwarding until it updated.

Browser extensions at least have a permission model. VS Code and Open VSX extensions run with the user's full privileges, which is why we cover IDE extension security separately.

The open problem is not review quality. It is that the update channel is invisible. Package managers now let teams hold new releases for a set time with a dependency cooldown. Browsers offer managed fleets no equivalent, no notice when an extension changes owner, and no diff between the version an administrator approved and the version running today. Until they do, the version on the endpoint is the only thing worth judging.