Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Provide a secure drop-in replacement for window.postMessage() #77

Open
carlosjeurissen opened this issue Sep 8, 2021 · 6 comments
Labels
enhancement Enhancement or change to an existing feature proposal Proposal for a change or new feature

Comments

@carlosjeurissen
Copy link
Contributor

carlosjeurissen commented Sep 8, 2021

This is proposed in the past by Palant. See CrBugReport. He found several security vulnerabilities related to the less secure postMessage API in extensions. And concluded a drop-in replacement is a must have.

Extension content scripts will often use window.postMessage() for internal communication within a tab. This is almost always a security issue. On the one hand, this communication is visible to the website and might leak private information. On the other hand, it’s impossible to distinguish legitimate messages from those sent by the website. Examples of security issues this introduces:

No amount of educating extension developers about runtime.sendMessage() is going to help here. This alternative is just too complicated when you merely need to send a message to another frame in the same tab: you need code in the background page to forward your messages, and you need to somehow figure out the frame ID of the target frame. This is complicated and error-prone, so it is unsurprising that developers choose window.postMessage() given how it “just works.”

A simpler API could help here, even if it is merely JavaScript boilerplate wrapping runtime.sendMessage() – as long as extension developers don’t need to write that boilerplate code themselves. Something like this:

browser.runtime.onFrameMessage.addListener((message, senderWindow) => {
  if (message == "ping") {
    browser.runtime.sendFrameMessage(senderWindow, "pong");
  }
});

browser.runtime.sendFrameMessage(iframe.contentWindow, "ping");

The important properties here: communication isn’t visible to website or other extensions, incoming message are guaranteed to originate from content scripts of the same extension.

@xeenon xeenon changed the title Proposal: Provide a secure drop-in replacement for window.postMessage() Proposal: Provide a secure drop-in replacement for window.postMessage() Sep 9, 2021
@xeenon xeenon added the enhancement Enhancement or change to an existing feature label Sep 9, 2021
@Rob--W
Copy link
Member

Rob--W commented Sep 30, 2021

Once an API has been introduced to get the frameId of a frame (#12), it becomes more feasible to introduce an extension API to allow communication between content scripts in a tab.

@carlosjeurissen As mentoned in #89 (meeting notes of today's meeting), you were going to file an issue about globally unique frameIDs, right? Otherwise it may be somewhat difficult to communicate between frames in different tabs (e.g. chrome.runtime.getFrameId(window.opener) would return a frameId, but it could be in a different tab)

@carlosjeurissen
Copy link
Contributor Author

carlosjeurissen commented Oct 1, 2021

@Rob--W Correct. See #90
If all frameIds are unique, we can use them for cross frame communication as well. Going for a syntax like:

browser.runtime.sendFrameMessage(frameId, "ping");

Or update sendMessage to also allow a target object instead of an extensionId. So you get:

browser.runtime.sendMessage({
  frameId: someFrameId
}, "ping");

And the extension target could then be specified like this:

browser.runtime.sendMessage({
  extensionId: someFrameId
}, "ping");

@dotproto
Copy link
Member

dotproto commented Oct 7, 2021

I've opened an issue on CRBug to track the runtime.getFrameId proposal on the Chromium side: https://bugs.chromium.org/p/chromium/issues/detail?id=1256872

Edit 2021-10-11: Whoops, meant to comment in issue 12. Thanks for covering me, @carlosjeurissen!

@fregante
Copy link

it may be somewhat difficult to communicate between frames in different tabs (e.g. chrome.runtime.getFrameId(window.opener) would return a frameId, but it could be in a different tab)

As mentioned in #12, exposing an API to get both the frame and the tab ID would be beneficial here too.

@xeenon xeenon added the proposal Proposal for a change or new feature label Aug 31, 2022
@tophf
Copy link

tophf commented Sep 12, 2022

Such a replacement must support transfer parameter, same as postMessage, to send super huge arraybuffer instantly.

FWIW, there's a simple method of secure communication shown here. TLDR it creates the iframe with a random token in the URL inside a closed ShadowDOM so the site cannot see it, then sends MessageChannel's port into the iframe that will be accepted only if the message equals that random token, then this port will be used for all communication. Of course this is also slightly convoluted, hence it won't be used by 99% of extensions.

@rektide
Copy link

rektide commented Sep 29, 2022

Along with @tophf I came up with a similar trivially simple securement idea myself, having just reading the description of this bug.

The one thing I would absolutely hate to see happen is that we break & disable the deliberate ability for the page & extensions to communicate in ad-hoc manners. Extensions right now have the capability & right to listen to the page, and the page has the right to communicate to them. This is not a bug! I'm tentatively ok with adding something more secure, but it would be horrific if we were to unweave the broader web extension capability with a an aggressively retrogressive alternative that does far less.

No amount of educating extension developers about runtime.sendMessage() is going to help here.

I dunno. I think adding evt.source !== window || evt.data.source !== "react-devtools-content-script" (as react-devtools-extensions does, originally via owasp presentation on this topic) is really not that impossible to teach or add. Have you tried? What documentation is there to current extension developers indicating this hazard?

Not an insufficient argument to say no, but, have we seen this be an issue yet? Absolutely, extensions should have more control than the page. There seem to be a good number of easy to do options here. I'm interested in this capability, in offering better securement, but the web already seems to offer a very clear securement channel. I'm afraid that trying to go further might run afoul of the Low Level Extensibility Manifesto, by offering some kind of high level option that might not really be as well suited as we might desire, and might cut off the more general platform capability that already permits fine security (with extremely small lift, albeit lift many aren't informed about).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement or change to an existing feature proposal Proposal for a change or new feature
Projects
None yet
Development

No branches or pull requests

7 participants