DEV Community

Cover image for Your Browser Extensions Are a Security Hole — Here's How to Audit Them
Alan West
Alan West

Posted on

Your Browser Extensions Are a Security Hole — Here's How to Audit Them

Last month I did something I should have done years ago: I opened chrome://extensions, looked at the permissions list on my installed extensions, and nearly choked on my coffee.

A tab manager extension had access to all my browsing data. A "simple" screenshot tool could read and modify every page I visited. A CSS picker extension — one I installed three years ago and forgot about — had permissions to access my clipboard and send network requests to domains I'd never heard of.

If you're a developer and you haven't audited your browser extensions recently, you're running untrusted code with alarming levels of access to your daily workflow. Let me walk you through how to actually fix this.

Why Browser Extensions Are a Massive Attack Surface

The core problem is simple: browser extensions run with elevated privileges inside the same environment where you do everything — access GitHub repos, manage cloud infrastructure, log into production dashboards, handle customer data.

A malicious or compromised extension can:

  • Read every form input on every page (including password fields)
  • Intercept authentication tokens and session cookies
  • Inject scripts into pages you trust
  • Exfiltrate data to external servers
  • Modify page content without any visual indication

The Chrome Web Store does review extensions, but the process isn't foolproof. Extensions get sold to new owners who push malicious updates. Dependencies get compromised. And some extensions just quietly collect more data than they need because their business model depends on it.

Step 1: Audit Your Current Extensions

Start by checking what you actually have installed and what permissions each extension holds.

Navigate to chrome://extensions (or about:addons in Firefox) and enable Developer Mode. For each extension, click "Details" and review the permissions. Here's what to watch for:

// RED FLAGS in extension permissions:
"Read and change all your data on all websites"  // nuclear option — very few extensions need this
"Manage your downloads"                          // can drop files onto your machine
"Read your browsing history"                     // tracking goldmine
"Communicate with cooperating native applications" // can talk to local processes
Enter fullscreen mode Exit fullscreen mode

Then do a quick cleanup. Remove anything you don't actively use. I went from 14 extensions down to 5 — and honestly, I should have done it sooner.

For the ones you keep, check if they're open source. If they are, you can actually read the code before trusting it. If they're closed source and request broad permissions, that's a judgment call you should make deliberately, not by default.

Step 2: Read the manifest.json

Every Chrome extension has a manifest.json file that declares its permissions. This is the first thing to check when evaluating any extension's source code.

{
  "manifest_version": 3,
  "name": "My Extension",
  "permissions": [
    "activeTab",
    "storage"
  ],
  "host_permissions": [
    "https://specific-api.example.com/*"
  ],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Here's how to read this:

  • permissions: API-level access. activeTab is relatively safe (only activates on the current tab when you click). tabs, webRequest, cookies — those are heavier.
  • host_permissions: Which websites the extension can interact with. <all_urls> means everything. A well-scoped extension targets only the domains it actually needs.
  • content_scripts with matches: ["<all_urls>"]: This extension injects JavaScript into every page you visit. That script can read the DOM, intercept form submissions, and modify page content.

The principle of least privilege applies here. A good extension requests only what it needs. A screenshot tool shouldn't need cookies permission. A dark mode toggle shouldn't need webRequest.

Step 3: Build Your Own (It's Easier Than You Think)

Here's the thing that surprised me: building a basic Chrome extension is genuinely straightforward. If you find yourself relying on a sketchy extension for something simple, consider just building it yourself.

Here's a minimal extension that does something actually useful — adds a button to copy the current page's title and URL as a markdown link:

// background.js
chrome.action.onClicked.addListener(async (tab) => {
  // Format as markdown link
  const markdown = `[${tab.title}](${tab.url})`;

  // Use the offscreen API to copy to clipboard
  // (Manifest V3 removed direct clipboard access from service workers)
  await chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: (text) => {
      navigator.clipboard.writeText(text);
    },
    args: [markdown]
  });
});
Enter fullscreen mode Exit fullscreen mode

The manifest for this only needs:

{
  "manifest_version": 3,
  "name": "Copy as Markdown Link",
  "version": "1.0",
  "permissions": ["activeTab", "scripting"],
  "action": {
    "default_title": "Copy as Markdown Link"
  },
  "background": {
    "service_worker": "background.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

Load it via chrome://extensions → "Load unpacked" and point to your folder. That's it. No build step, no bundler, no store submission required.

The activeTab permission here is key — it only grants access to the tab the user explicitly clicks on, and only at the moment they click. Compare that to an extension that requests <all_urls> to do the same thing.

Step 4: Automate Auditing with a Script

If you want to go deeper, you can script the audit process. Chrome stores extension data in a predictable location:

# macOS
ls ~/Library/Application\ Support/Google/Chrome/Default/Extensions/

# Linux
ls ~/.config/google-chrome/Default/Extensions/

# For each extension, check its manifest
for dir in ~/Library/Application\ Support/Google/Chrome/Default/Extensions/*/; do
  latest=$(ls -t "$dir" | head -1)
  manifest="$dir$latest/manifest.json"
  if [ -f "$manifest" ]; then
    name=$(python3 -c "import json; print(json.load(open('$manifest')).get('name','unknown'))")
    perms=$(python3 -c "import json; print(json.load(open('$manifest')).get('permissions',[]))")
    hosts=$(python3 -c "import json; print(json.load(open('$manifest')).get('host_permissions',[]))")
    echo "Extension: $name"
    echo "  Permissions: $perms"
    echo "  Host access: $hosts"
    echo ""
  fi
done
Enter fullscreen mode Exit fullscreen mode

This gives you a quick dump of every extension and what it's requesting. Pipe it to a file and review it over coffee. You might be surprised what you find.

Prevention: Ground Rules Going Forward

After going through this process, I set some rules for myself:

  • Prefer open-source extensions. If I can read the source, I can verify what it does. Several developers have started publishing extensions as open-source GitHub repos specifically to address trust issues — and that's a trend worth supporting.
  • Check update frequency and ownership. An extension that changed hands six months ago and suddenly pushed three updates? Suspicious.
  • Use separate browser profiles. My "work" profile has minimal extensions. My casual browsing profile is where I experiment. Production credentials and sketchy extensions should never share a profile.
  • Pin extension versions when possible. If you're loading an unpacked extension from source, you control when it updates.
  • Review permissions after every update. Extensions can request new permissions in updates. Chrome will sometimes prompt you, but not always for every permission change.

The Bigger Picture

The browser extension ecosystem has a trust problem that mirrors what we've seen with npm packages, VS Code extensions, and basically every plugin marketplace. The attack surface is enormous, the review processes are imperfect, and most users never look under the hood.

As developers, we're in a unique position — we can actually read the code. We can audit manifest.json files. We can build our own replacements for simple utilities. And when we find well-maintained, open-source alternatives to closed-source extensions, we should prefer them.

I'm not saying you need to build everything from scratch. But you should at least know what code is running in your browser with access to your most sensitive workflows. Take thirty minutes this week and audit your extensions. Future you will appreciate it.

Top comments (0)