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.onMessageis unauthenticated by default — every handler must explicitly validatesender.idagainstchrome.runtime.idbefore trusting any message content. -
Content script origin requires separate verification —
sender.idalone is insufficient when messages may originate from injected scripts; thesender.urlmust 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.