Back to Blog
critical SEVERITY4 min read

chrome.runtime.onMessage: Missing Sender Validation in Extension

A critical authentication flaw in a Chrome extension's message handling allowed arbitrary extensions and malicious web pages to trigger sensitive operations including script injection and tab manipulation. The vulnerability existed because chrome.runtime.onMessage listeners processed requests without verifying sender identity or origin, trusting any message source by default.

O
By Orbis AppSec
•Published September 25, 2026•Reviewed September 25, 2026

Answer Summary

The affected code is a Chrome extension's background script handling chrome.runtime.onMessage events without sender validation. An attacker with a malicious extension or compromised web page could inject scripts into TikTok tabs, create arbitrary tabs, and manipulate the extension's internal job state. The fix adds explicit validation: messages are rejected unless sender.id matches chrome.runtime.id, and for tab-originating messages, the URL must match https://tiktok.com. CWE-287 (Improper Authentication).

Vulnerability at a Glance

cweCWE-287
fixAdded sender.id and origin URL validation before processing any message action
riskMalicious extensions or compromised origins trigger privileged operations
languageJavaScript
root causechrome.runtime.onMessage listener accepted all messages without sender validation
vulnerabilityMissing authentication on inter-extension messaging

Affected Versions

Affected not applicable (first-party code)
Fixed in not applicable (first-party code) — see PR for fix commit
Ecosystem Chrome Extension (Manifest V3)
CVE / GHSA not assigned
CWE CWE-287: Improper Authentication

The Vulnerability Explained

Chrome extensions rely on chrome.runtime.onMessage for internal communication between background scripts, content scripts, and popup pages. By design, this API accepts messages from multiple sources: other extension contexts, content scripts injected into web pages, and even other extensions if permissions allow. The critical mistake here was assuming message receipt implies trust.

The vulnerable code processed sensitive actions immediately upon message receipt:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "runFinished") {
    clearStoredActiveJob(sender.tab && sender.tab.id, () => sendResponse({ ok: true }));
    return true;
  }
  // ... handlers for startRemovingFavorites, injectPageRemoveListener, etc.
});

The sender parameter contains identity metadata that was completely ignored. An attacker could exploit this through two vectors:

Cross-extension attack: Any installed extension with externally_connectable permissions or the ability to message other extensions could send forged startRemovingFavorites or injectPageRemoveListener commands. The handler would execute chrome.scripting.executeScript or chrome.tabs.create under the victim extension's elevated permissions.

Compromised origin attack: The extension injects content scripts into TikTok pages. If a TikTok tab were compromised through XSS or if a malicious site framed TikTok content, that JavaScript could message the background script directly. The sender.tab property would be populated, making the message appear legitimate without actual extension authorization.

The real impact extends beyond irritation: chrome.scripting.executeScript allows arbitrary code execution in any tab the extension can access. An attacker could pivot from messaging to full tab compromise, steal TikTok session data, or manipulate the extension's proprietary API orchestration logic.

The Fix

The patch adds authentication at the message entry point, before any action handler executes:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (!sender || sender.id !== chrome.runtime.id) return false;
  if (sender.tab && !/^https:\/\/(www\.)?tiktok\.com\//.test(sender.url || "")) return false;

  if (request.action === "runFinished") {
    clearStoredActiveJob(sender.tab && sender.tab.id, () => sendResponse({ ok: true }));
    return true;
  }
  // ... remaining handlers
});

The first line implements identity validation: sender.id must match chrome.runtime.id, which is cryptographically bound to the extension's signing key. This blocks all cross-extension messages and any web-originated messages that lack proper extension context.

The second line adds origin validation for content script messages: when sender.tab exists, the URL must match the TikTok domain pattern. This prevents a compromised content script on an attacker-controlled site from messaging the extension—even if somehow injected with the extension's ID.

Returning false immediately for failed validation is idiomatic: it tells Chrome's message port that no asynchronous response will follow, allowing cleanup.

Key Takeaways

  • chrome.runtime.onMessage is unauthenticated by default — every handler must explicitly validate sender.id against chrome.runtime.id before trusting any message content.

  • Content script origin requires separate verification — sender.id alone is insufficient when messages may originate from injected scripts; the sender.url must match expected patterns.

  • Sensitive APIs compound messaging risks — chrome.scripting.executeScript, chrome.tabs.create, and cross-origin API calls should never be reachable without cryptographic identity proof.

  • Early validation prevents handler sprawl — placing checks at the listener entry point, rather than in each action handler, eliminates the risk of inconsistent validation across message types.

How Orbis AppSec Detected This

Source: The request parameter from chrome.runtime.onMessage events, containing arbitrary action commands from any caller.

Sink: chrome.scripting.executeScript, chrome.tabs.create, and TikTok API orchestration calls triggered by message handlers including startRemovingFavorites and injectPageRemoveListener.

Missing control: No validation of sender.id against chrome.runtime.id, and no verification that sender.tab.url matches expected origins before executing privileged operations.

CWE: CWE-287 — Improper Authentication. The extension failed to authenticate message sources before acting on their contents.

Fix: Added explicit sender identity verification requiring sender.id === chrome.runtime.id and origin URL pattern matching for tab-originating messages.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

This vulnerability illustrates a systemic pattern in browser extension security: the convenience of chrome.runtime.onMessage creates an implicit trust boundary that developers often fail to harden. The fix demonstrates proper defense-in-depth—cryptographic identity verification plus origin whitelisting—applied at the earliest possible point. For extension developers, the lesson is mechanical: every onMessage listener must begin with sender validation, or it becomes a privileged API exposed to the entire extension ecosystem and any compromised web origin.

Prevention and further reading

Frequently Asked Questions

Why does the fix check sender.tab separately from sender.id?

The extension handles messages from both internal extension contexts (no tab) and content scripts injected into TikTok pages (has tab). The sender.id check verifies extension origin; the tab URL check verifies web origin when applicable.

Could a malicious TikTok page exploit this before the fix?

Yes. Any JavaScript executing on tiktok.com could send messages to the extension's background script. The fix restricts this to the extension's own content scripts by requiring sender.id to match chrome.runtime.id.

What happens to messages that fail the new validation?

The handler returns false immediately, which signals to Chrome that the message will not be processed and no response will be sent. This silently drops unauthorized requests.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #8

Related Articles

critical

boardroom Server Handler Missing Authentication on HTTP Endpoints

The boardroom server's `Handler` class, extending `SimpleHTTPRequestHandler`, exposed sensitive HTTP endpoints without any caller authentication. An attacker could exploit this by making cross-origin requests to internal ports through DNS rebinding, accessing `/alerts.json`, `/dismiss`, and `/events.js` without authorization. The fix adds `is_local_origin()` checks to both `do_GET()` and `do_POST()` methods.

critical

docker_rpc.uc Command Injection: Unsanitized RPC Parameters

A critical command injection vulnerability in the Docker RPC handler allowed authenticated attackers to execute arbitrary system commands by injecting shell metacharacters into container ID, port, user ID, or command parameters. The fix validates all user-supplied inputs against strict whitelist patterns before interpolating them into shell commands.

critical

JWT Authentication Disabled Signature Validation in

A critical misconfiguration in JWT authentication explicitly disabled signature validation, allowing attackers to forge valid tokens with arbitrary claims and bypass authentication entirely. The fix re-enables signature validation on all incoming bearer tokens, restoring the security boundary of the authentication layer.

critical

How Hardcoded Secrets Compromise Authentication in JavaScript and How to Fix It

A critical vulnerability in `Tool/QuantumultX/Rewrite/RRSP.js` exposed hardcoded API authentication credentials—a TOKEN and UMID device identifier—directly in source code. Anyone with repository access could extract these credentials to impersonate the legitimate user and gain full account access to the RRTV API service. The fix replaced hardcoded secrets with empty placeholders, forcing users to manually configure credentials through secure channels.

critical

How origin validation bypass happens in Express.js and how to fix it

A `POST /changeData` route in `src/main/server/routes/index.js` guarded state-changing writes with an origin allowlist, but the guard was wrapped in an `if (origin && ...)` truthiness check. Any request that simply omitted both `Origin` and `Referer` — a one-line `curl` command, a local script, a background process — skipped validation entirely and modified application data. The fix removes the truthiness short-circuit so a *missing* header is now treated as a rejection, not a pass.

critical

How CSRF vulnerabilities happen in Node.js API clients and how to fix them

A critical CSRF vulnerability in `lib/client.js` allowed attackers to forge authenticated POST requests to `/remote-ssh/api/*` endpoints. The fix adds the `X-Requested-With: XMLHttpRequest` header to enable proper CSRF token validation, blocking malicious cross-site requests with a minimal one-line change.