Your Browser Extensions Keep Their Permissions Forever. That's the Whole Vulnerability.

Cover Image for Your Browser Extensions Keep Their Permissions Forever. That's the Whole Vulnerability.

On December 24, 2024, someone on Cyberhaven's team opened an email that looked like it came from Chrome Web Store Developer Support, warning that the company's extension was about to be pulled for a policy violation. They clicked through and authorized what looked like a routine Google permissions screen — actually a grant to a malicious OAuth app called "Privacy Policy Extension." By Christmas morning, a modified version of Cyberhaven's own browser extension, installed by more than 400,000 people specifically to keep sensitive data from leaking out of their browsers, was quietly exfiltrating Facebook Ads session cookies and API tokens to a server the attacker controlled.

Nobody clicked "allow" on anything new. The extension had already been trusted. It just changed what it did with that trust.

That's the detail everyone skips when they write this up as a phishing story. Phishing is the easy part — it's the entry point every attacker already knows works, on every platform, forever. The actual vulnerability is what happens after: a browser extension earns broad, standing runtime permissions once, during its very first review, and keeps them for the rest of its existence. Every auto-update re-installs that trust silently, with no fresh consent from the person whose browser it's running in. The permission model has no concept of "the entity I trusted a year ago is not necessarily the entity shipping code today." That's not a phishing problem. That's an architecture problem, and it's been sitting there for years.

What actually happened when Cyberhaven's extension went bad

Cyberhaven wasn't an isolated hit. Security researchers who dug into the campaign found the same phishing template — a fake "Developer Program Policy violation" notice pushing victims toward an OAuth consent screen — used against a wave of extension developers stretching back to mid-November 2024. RH-ISAC's writeup put the confirmed toll at more than 35 compromised extensions and over 2.6 million exposed users, with attackers going after ChatGPT session tokens and Facebook for Business credentials specifically because those unlock advertising accounts attackers can immediately monetize. Reporting from The Hacker News traced the same OAuth-phishing signature across more than a dozen separate extension publishers in a single month.

Google pulled Cyberhaven's malicious build within about an hour of the incident being flagged publicly on December 25 — fast, but reactive. Nothing in the Chrome Web Store's own review pipeline caught it before it shipped to users. It was caught the way most of these are caught: outside researchers noticed the traffic first.

The Great Suspender proved this in 2021 and nobody fixed the model

If you want to know this is structural rather than a 2024 fluke, go back further. The Great Suspender was a wildly popular Chrome extension — more than two million installs — that froze inactive tabs to save memory. Its original developer sold it to an unknown third party. In version 7.1.8, the new owner added code capable of executing arbitrary remote commands and committing ad fraud. 9to5Google's coverage of the takedown documents Google's response: it didn't just delist the extension, it remotely force-disabled it in every installed browser, because there was no other way to sever two million people from code they'd never re-approved.

Notice the pattern repeating: no new install event, no new permission prompt, no action from the user at all. Ownership changed hands, a version number ticked up, and an extension trusted by millions became a different program overnight. The Cyberhaven attackers didn't invent this mechanism. They just found a faster way into it — phishing a login instead of buying a company — and got the same result.

Why the Chrome Web Store review can't catch this

Google's own documentation on the Chrome Web Store review process states plainly that "all item submissions — whether for a new item or an update to an existing one — are subject to the same review process," using a mix of automated and manual checks, with extra scrutiny reserved for submissions requesting dangerous new permissions like broad host access. That sounds thorough until you notice what it's actually built to catch: a new extension asking for something it shouldn't, or an existing one requesting a permission it didn't have before.

It is not built to catch a previously-clean, previously-approved developer account pushing a build that adds a hidden exfiltration routine without touching the declared manifest permissions at all — which is exactly what happened with Cyberhaven's extension. The malicious version 24.10.4 was a modified copy of clean code with additional logic bolted on, and it required no new permission grant to run, because the extension already had access to everything it needed.

Firefox's model has the same soft spot from a different angle. Mozilla's own Extension Workshop documentation confirms that new versions run through an automated linter, then get "signed and listed on AMO immediately" — human review only happens if a submission gets flagged for one. Most updates on both major browsers ship on the strength of automated pattern-matching against a version of the extension nobody re-reads line by line. That's not a criticism of the review teams. It's an admission that manual review doesn't scale to the update velocity extensions actually ship at, so the system defaults to trusting the account, not re-verifying the code.

npm's Shai-Hulud worm shows this isn't a browser problem

The same mechanism runs the entire modern package ecosystem, and it just proved itself at much larger scale. In September 2025, a self-propagating worm known as Shai-Hulud compromised npm maintainer accounts and spread through dependency trees automatically. CISA's advisory confirms the scope: a widespread compromise touching packages including chalk, debug, ansi-styles, and strip-ansi — libraries that, combined, account for billions of weekly downloads across the JavaScript ecosystem. The initial foothold, per multiple incident writeups, came from phishing the maintainer of chalk with a fake npm-support domain and a manufactured 2FA-reset deadline — the exact same urgency-plus-credential-theft playbook used against Cyberhaven, just aimed at a package registry instead of a browser store.

The tooling looks different. The failure mode is identical: one compromised publisher account, one silent update, zero new consent required from the millions of downstream consumers who'd already decided to trust that name months or years earlier. It's worth noticing this blind spot shows up everywhere security teams draw a hard line between "code I reviewed" and "code currently executing" — I've written before about how LLM application security has the same category error, testing for a threat model that assumes a fixed boundary the system no longer actually has.

So actually, the fix isn't more scanning — it's an expiration date on trust

The industry's answer to every one of these incidents is more detection: better static analysis on extension code, anomaly detection on OAuth grants, tighter developer-account MFA requirements. All useful. None of it addresses the actual defect, which is that browser extension trust has no expiration date and no re-confirmation trigger tied to anything except a manifest permissions diff.

Here's the fix nobody's shipping: permission grants that don't outlive the specific, hash-verified build that earned them. Any update — not just ones that touch the declared API surface — should require the browser to show the user "this extension changed" and force a fresh, lightweight confirmation, the same way a re-auth prompt works for a banking app after a period of inactivity. Right now, a diff that adds ten lines of obfuscated exfiltration code but touches zero manifest permissions is invisible to the review pipeline and invisible to the user. From the platform's perspective, nothing changed. From the user's perspective, it's a different program wearing a familiar icon.

Vendors keep selling scanners. Scanners inspect code at a single point in time and then go quiet. The vulnerability isn't a gap in scanning coverage — it's that "reviewed once" and "trusted forever" got treated as the same thing, in a system that updates itself in the background every few days.

Every extension sitting in your toolbar right now was approved once, by a reviewer who may not work there anymore, looking at code that isn't the code currently running. When's the last time you actually checked what changed?