Open Bug 1319168 Opened 8 years ago Updated 2 months ago

Implement externally_connectable from a website

Categories

(WebExtensions :: Compatibility, enhancement, P3)

enhancement

Tracking

(Not tracked)

People

(Reporter: rpl, Unassigned)

References

(Blocks 1 open bug)

Details

(Whiteboard: [triaged][chrome][addons-jira])

The browser.runtime's onConnectExternal/onMessageExternal API methods, besides being used to make a WebExtension "externally_connectable" from a different addon (which was already being discussed and tracked by Bug 1258360), can be used to make a WebExtension "externally_connectable" from a website.

This issue will focus on the "externally_connectable from a website" part of onConnectExternal/onMessageExternal (evaluating what its behavior should be, discuss any concerns related to possible threats, and track its implementation).
See Also: → 1258360
It interesting to notice that being able to be "externally connectable from a website" seems to be much more requested and much more used by addon developers, probably one reason is that it would let them to integrate with a website without injecting a content script in every webpage of that website (or of any website, which means that the description of the addon permissions that is going to be presented to the user will not contain a "can read everything on websiteX", but something that is going to sound less scary, like "websites can send messages to this addon")

For the same reason, it seems that an addon developer can prefer to let any website to be able to connect to their addon (which seems to not being currently possible in chrome: https://github.com/MetaMask/metamask-plugin/issues/813#issuecomment-261363677)
Some initial thoughts about some potential concerns/threats that we may want to discuss before providing this feature:

- should/can we prevent the content script of a different addon from using the messaging API provided to the website from an "externally_connectable addon"?

- should/can we prevent code that is running in the webpage but is not originated by the same origin (e.g. an injected script tag from a different domain) from using the messaging API provided to the webside from an "externally_connectable addon"?
Whiteboard: [advisory-group]
Adding note to myself to find some numbers of how much this is used in Chrome add-ons to communicate from a webpage to an add-on.
Flags: needinfo?(amckay)
A quick scan of 57804 Chrome extensions shows:

1837 externally_connectable                        // number of extensions with this manifest key
  22 externally_connectable_accepts_tls_channel_id
   1 externally_connectable_ids average            // average number of ids per definition
 114 externally_connectable_ids
  75 externally_connectable_ids_*                  // number of extensions that use *
   2 externally_connectable_matches average        // average number of matches per definition
1812 externally_connectable_matches                // extensions that define a 
   2 externally_connectable_matches_*              // number of extensions that use <all_urls>
Flags: needinfo?(amckay)
Flags: needinfo?(kmaglione+bmo)
Whiteboard: [advisory-group]
Whiteboard: triaged
Hi There, sorry for not chiming in sooner, as the discussion was somewhat spurred by my web extension! (Thanks Luca! We've been busy, as I'm sure you have been!)

We just finally started identifying our pain points with Chrome's Extension API, and started a discussion with them, and so it seemed like a good time to warm up the iron on this side too.

I think Luca's security concerns are valid, but could be addressed with security policies, for example a safe and very restrictive policy might require that injected APIs of that type are not able to send messages on behalf of the page, or issue http/fetch requests from the site's origin domain, or maybe are only able receive function calls from the page, and can only communicate with their own Extension's context (although we extensions would love to talk to each other too).

If our injected API was merely able to pass its messages to the content script or background script, and return the responses, that alone would be incredibly powerful, granting the ability to add new APIs to the browser via nothing but an extension.

This is exactly what MetaMask is today, but it's not easy. We're injecting "web3", which is an immature, and very rough API for interacting with some peer-to-peer networks like the Ethereum blockchain. MetaMask is allowing developers to experiment with patterns of developing on these peer to peer networks, and the experience we've gotten from even our rough current version has spurred some great discussion & design around the next iteration of the API.

Andy: If you wanted even more interesting numbers, check how many users each of those extensions has. Just out of curiosity, I don't actually think this is a very common pattern yet.
Priority: -- → P3
Andy: From your quick scan of the 1837 Google Chrome extensions that contains "externally_connectable", around 175 of those extensions are mine.

As this is currently a P3, I'll assume this implementation will not land into 57 release and will follow something like this as an alternative : https://stackoverflow.com/questions/10526995/can-a-site-invoke-a-browser-extension/10527809#10527809
Correct, this won't land in 57.
Blocks: 1392434
Update: since Andy posted comment 4 one year ago, the number of Chrome extensions that use externally_connectable has grown to 3,735 out of 82,659 extensions, or 4.52%, up from 3.18% in comment 4.
Severity: normal → enhancement
Adding to browser compatibility pages https://github.com/mdn/browser-compat-data/pull/2329
Product: Toolkit → WebExtensions
Flags: needinfo?(kmaglione+bmo)
Whiteboard: triaged → [triaged][chrome]
I am developing a website that communicates with an extension that is implemented to extend the functionality of this website. The website and the extension sometimes need to exchange sensitive data. Chrome extensions can be made 'externally_connectable'. Using this mechanism I can assure that from the point of view of my website I am communicating with the right extension by using the id of the extension.

In Firefox however I need to fall back on CustomEvent or window.postMessage which technically works ok, but does not guarantee me that I am communicating with the right extension. By using CustomEvent or postMessage any other extension can possibly eavesdrop on the [website - extension] connection.

I'd like to add to this discussion that adding the 'externally_connectable' feature is significantly adding to the secureness of this kind of extensions.
Flags: needinfo?(ddurst)
Flags: needinfo?(ddurst)

The l10n team is evaluating new translation management systems for our vendor pipeline. One of the candidates (Lingotek) has an extension for performing in-context localization on websites. This feature could reduce l10n turnaround time and increase quality of localizations for marketing campaigns on mozilla.org. Shane, how do we go about raising the priority of this bug? What additional information do you need?

Flags: needinfo?(mixedpuppy)

I don't see us being able to fit this in. IIUC This also requires exposing an api to content which will have a very high bar. cc/Philipp to see what he thinks.

Flags: needinfo?(mixedpuppy) → needinfo?(philipp)

My take on the security concerns detailed in comment #2:

  • should/can we prevent the content script of a different addon from using the messaging API provided to the website from an "externally_connectable addon"? Chrome's implementation won't allow to send messages to extensions from content scripts, so if implemented the same way, this shouldn't be an issue.

  • should/can we prevent code that is running in the webpage but is not originated by the same origin (e.g. an injected script tag from a different domain) from using the messaging API provided to the webside from an "externally_connectable addon"? This is indeed possible in Chrome's current implementation and sender.origin returns the host's page, rather than the origin of the injected script, so you can't even check that. My hunch here is that it's up to the host page to protect itself from cross-site scripting and shouldn't be relevant here. I'd argue that externally_connectable is still more secure than the known workarounds, as reported in comment #11.

Regarding collecting numbers of how many extensions are using this, please note that the absence of "externally_connectable" from a codebase might just mean that people are using workarounds in order to support Firefox and don't bother maintaining two separate implementations. If it wasn't for the added security of externally_connectable, I'd consider that myself.

So, please, consider adding this. 🙏

externally_connectable does not offer the security guarantees that comment 18 and comment 11 describe. The answer to comment 2 is "No, we cannot":

  • Extensions with the right permissions can run scripts in web pages, and that code is indistinguishable from pre-existing code in the web page. Therefore any attempt to prevent other extensions from communicating with the web page is futile. Another result is that extensions that listen to messages from externally_connectable cannot tell with certainty that the message originates from a specific website (opposed to a different extension).

  • There is no meaningful distinction between inline scripts, same-origin scripts and cross-origin scripts - ultimately they all run in the same context.

Thanks for chiming in, Rob Wu. Feel free to push back, but for what I can tell, if my extension doesn't allow other extensions to send me messages, they can't send them even from content scripts.

With the following manifest.json, only *somesite.com can send me messages:

"externally_connectable": {
    "matches": ["https://*.somesite.com/*"]
}

To allow another extension (even from content scripts), I would have to add the ids array. Without it, the message doesn't hit my extension.

This is even clearer when you think that when I allow an extension, its message has a sender.id that identifies the extension even from a content script.

So, to the best of my knowledge, external message sources are whitelisted on the receiving end. Everything else won't reach it.

My use case is similar to comment #11. I want to communicate data from the website to the extension. With externally_connectable:

  • I can be sure that the message comes indeed from the expected website
  • an injected script wouldn't have that data anyway (it lives in the memory of the website, it's not stored in localStorage or cookies)

On the other hand, using a postMessage or a CustomEvent an injected script could eavesdrop and steal it.

From the MDN page:

Externally connectable allows extension developer to control which other extensions and web pages can communicate with this extension via runtime.connect() and runtime.sendMessage() message passing. If externally_connectable is not specified, all extensions can communicate with each other but not with web pages.

I was able to confirm this in my tests.

Now, sending messages from the extension to webpages or content scripts is another matter altogether and there you don't have any guarantee. From Chrome's developer page:

Assume any data sent to the content script might leak to the web page. Limit the scope of privileged actions that can be triggered by messages received from content scripts.

But this is not my use case and it doesn't invalidate my assumptions above.

If you know otherwise, please let me know. I am trying to implement the best solution for my users and, for what I can tell, externally_connectable is the better option (much simpler and cleaner, and more secure).

The lack of this option in Firefox will force me to maintain two different solutions and decrease security and privacy for my Firefox users.

(In reply to Emanuele Feliziani from comment #20)

I can confirm the issue described by Emanuele also applies to my extension that is ought to collaborate with a specific website. We recently had our extensions pentested and Firefox implementation was labeled again as less secure because of the clumsy implementation of the communication mechanism (by dispatching new CustomEvent(...))

(In reply to Emanuele Feliziani from comment #20)

Thanks for chiming in, Rob Wu. Feel free to push back, but for what I can tell, if my extension doesn't allow other extensions to send me messages, they can't send them even from content scripts.

With the following manifest.json, only *somesite.com can send me messages:

This is not the case. Extensions with host permissions can impersonate web pages (e.g. via content scripts or by rewriting HTTP responses with the webRequest API).

My use case is similar to comment #11. I want to communicate data from the website to the extension. With externally_connectable:

  • I can be sure that the message comes indeed from the expected website

correction: the expected website plus any other extension that has host permissions for that website.

  • an injected script wouldn't have that data anyway (it lives in the memory of the website, it's not stored in localStorage or cookies)

I don't understand what you mean by this.

On the other hand, using a postMessage or a CustomEvent an injected script could eavesdrop and steal it.

This is no different from the case with externally_connectable. Another extension could patch a web page's chrome.runtime.sendMessage method (or other APIs / scripts in the web page) to eavesdrop on messages.

From the MDN page:

Externally connectable allows extension developer to control which other extensions and web pages can communicate with this extension via runtime.connect() and runtime.sendMessage() message passing. If externally_connectable is not specified, all extensions can communicate with each other but not with web pages.

I was able to confirm this in my tests.

Note that this is Firefox's current behavior - without support for externally_connectable, only extensions can communicate with each other. Firefox does not add a chrome.runtime.sendMessage method to web pages.

Now, sending messages from the extension to webpages or content scripts is another matter altogether and there you don't have any guarantee. From Chrome's developer page:

Assume any data sent to the content script might leak to the web page. Limit the scope of privileged actions that can be triggered by messages received from content scripts.

But this is not my use case and it doesn't invalidate my assumptions above.

I think that you've misread the message of the documentation. This warning about extension -> web page does not mean that the other way around is secure. The broader context of the cited documentation is about compromised renderer processes (=the OS process that hosts the web page), with the threat being other (web) content hosted in the same process. The same principle applies to the scenario that I sketched before: when an extension is able to inject code in a web page, then that extension is able to forge and eavesdrop messages to/from the web page.

If you know otherwise, please let me know. I am trying to implement the best solution for my users and, for what I can tell, externally_connectable is the better option (much simpler and cleaner, and more secure).

The lack of this option in Firefox will force me to maintain two different solutions and decrease security and privacy for my Firefox users.

(In reply to timboektoe from comment #21)

(In reply to Emanuele Feliziani from comment #20)

I can confirm the issue described by Emanuele also applies to my extension that is ought to collaborate with a specific website. We recently had our extensions pentested and Firefox implementation was labeled again as less secure because of the clumsy implementation of the communication mechanism (by dispatching new CustomEvent(...))

Under the threat model of malicious extensions having been installed, externally_connectable (+web->extension messaging) is not more secure than CustomEvent (+inter-extension messaging). To emphasize: if your user has a malicious extension, then there is no reliable way for web pages to prevent tampering from that other extension. Consequently, extensions have to assume that any communication with a web page could have been tainted by other extensions.

Thanks for clarifying, Rob Wu. Now I have a better understanding of the issue. A script with full privileges can inject a script and thus appear as the webpage itself.

If I wanted to prevent this from happening:

  1. Could I use CSP to prevent script from other domains to be injected? Would this block extension-generated script tags or those are exempted?
  2. Could I use CSP hashes to prevent an extension from tampering with my original script, as explained here?

an injected script wouldn't have that data anyway (it lives in the memory of the website, it's not stored in localStorage or cookies)
I don't understand what you mean by this.

I mean that this data is known by my application, the third party can't know that. Even if it could forge a message, it would simply be an invalid message. Not much harm done. What I want to avoid is for the third-party to intercept my legitimate communication to my extension.

If this security advantage boils down to nothing, there is still the convenience advantage. The workarounds are rather clumsy and hacky.

Anyway, thanks for taking the time to explain things in detail. Much appreciated.

(In reply to Emanuele Feliziani from comment #23)

Thanks for clarifying, Rob Wu. Now I have a better understanding of the issue. A script with full privileges can inject a script and thus appear as the webpage itself.

If I wanted to prevent this from happening:

  1. Could I use CSP to prevent script from other domains to be injected?

Yes.

Would this block extension-generated script tags or those are exempted?

Extension resources are supposedly exempt from CSP, per https://www.w3.org/TR/CSP3/#extensions . moz-extension: and chrome-extension:-URLs cannot be blocked.

  1. Could I use CSP hashes to prevent an extension from tampering with my original script, as explained here?

Subresource integrity could be used to harden the scripts, but is not an effective defence against all extensions, because extensions can just modify the response to remove the CSP / hashes.

an injected script wouldn't have that data anyway (it lives in the memory of the website, it's not stored in localStorage or cookies)
I don't understand what you mean by this.

I mean that this data is known by my application, the third party can't know that. Even if it could forge a message, it would simply be an invalid message. Not much harm done. What I want to avoid is for the third-party to intercept my legitimate communication to my extension.

That is not possible. Third-party extensions with the right permissions can always intercept legitimate communication with your extension.

Thanka lot for commenting Rob Wu. Really appreciated. You say:

Under the threat model of malicious extensions having been installed, externally_connectable (+web->extension messaging) is not more secure than CustomEvent (+inter-extension messaging). To emphasize: if your user has a malicious extension, then there is no reliable way for web pages to prevent tampering from that other extension. Consequently, extensions have to assume that any communication with a web page could have been tainted by other extensions.

I was thinking that by explicitly adding the extension id as we do for Chrome the webpage is at least sure it is communicating with the right extension. I now realise that another malicious extension can tamper the code of the webpage and manipulate the extensionId by spoofing the extensionId in the js code of the site.

However, in case I encrypt the extensionId on the server side with a private key and decrypt the extensionID within the context of the webpage with a public key, would the solution then not be much more harder to tamper with compared to the CustomEvent approach? Like installing 2 locks on the front door instead of 1.

I was thinking that by explicitly adding the extension id as we do for Chrome the webpage is at least sure it is communicating with the right extension. I now realise that another malicious extension can tamper the code of the webpage and manipulate the extensionId by spoofing the extensionId in the js code of the site.

The malicious extension doesn't need to spoof anything. Anything readable to the web page is also readable to that malicious extension.
At some point, a web page that wishes to send a message to a specific extension has to hand off a plain text extension ID and message to a messaging API (e.g. chrome.runtime.sendMessage or dispatchEvent+CustomEvent). A malicious extension does not even need to put any effort in deciphering the initial input, all it needs to do is to intercept calls to these messaging APIs.

However, in case I encrypt the extensionId on the server side with a private key and decrypt the extensionID within the context of the webpage with a public key, would the solution then not be much more harder to tamper with compared to the CustomEvent approach?

Not at all. This is not having any positive impact on security. On the contrary, the unnecessarily complicated system is more likely going to have implementation errors that degrade the security. If your threat model includes malicious extensions, then you need to accept the risk of other extensions tampering with the content. If you want to completely rule out that possibility, then the core of the functionality should run in an extension page.

stale ni

Severity: normal → S4
Flags: needinfo?(philipp)

Hello,
I am looking for a way for my website to know that an extension with a particular ID is installed in the browser. And not be fooled by potential third-party copies of my extension.

From what I understand in this discussion, this may not be possible.
Even if my website had an API available to say "I want to talk to extension AAA"
A third party extension could easily inject a content script to modify the website code into "I want to talk to extension BBB".

Still, I'd like to ask, any thoughts or solutions in mind? Thanks

(In reply to Francois from comment #29)

Hello,
I am looking for a way for my website to know that an extension with a particular ID is installed in the browser. And not be fooled by potential third-party copies of my extension.

From what I understand in this discussion, this may not be possible.
Even if my website had an API available to say "I want to talk to extension AAA"
A third party extension could easily inject a content script to modify the website code into "I want to talk to extension BBB".

Still, I'd like to ask, any thoughts or solutions in mind? Thanks

Hi Francois, Rob Wu is right that it will always be possible to fool a website into thinking it is communicating with a certain extension. There can be a second extension installed that changes the code of your website in such a way that it alters the extension id in the js coming from your server. My idea was that I could make it a bit more difficult by hiding this extensionId in obfuscated/encrypted code etc. The company that did our security assessment advised us to do this. It will take some more time for someone to fool your website but in the end it is still possible.

Just checking to see if this is still something we can expect to see this in FF anytime soon?

I understand there's some unknowns regarding authenticity of messages originating from the website in question to the extension. Is this really a deal breaker? I've read some posts that as a work around extension developers are injecting a content script into the desired site that then communicates with the extension (I suppose the sandboxed nature of these scripts allows us to know with certainty that the communications from them are in fact authentic, because they are owned by the extension).

However, if developers are sufficiently aware of the associated risks of the system, is it a bit too opinionated to remove a simple way to communicate between site and extension that may be non-consequential? Thinking from the perspective of protecting an end-user....if you've given a malicious extension the ability to inject a content script "anywhere" you've already opened yourself up to potentially far worse vulnerability than that content script sending a forged message to another extension. For instance, they can inspect any data you type into any site (email, password, etc...). This is why extensions request permissions and undergo security reviews to ensure they are not acting maliciously (at least in chrome, i'm not sure of what if any review process FF uses).

That's My 2 cents :D

TL;DR We use externally connectable to provide some easy helpers from our web app to the extension and we're looking to release in FF. So just trying to determine if I need to retool our current system to support FF or not :D

I agree with ericuldall. We are now using CustomEvents in a content script to communicate with the extension which feels crippled. Using externally connectable my website will be able to directly communicate with the extension's background script. A much cleaner approach. That's my 2 cents as well :-)

I agree with whole discussion and concerns about security related risks.

Anyway if FF will not support the API method maybe that should be added as an error (or at least a warning) in https://www.extensiontest.com/. Up to this moment I don't see trace of incompatibility references in the generated report. I only see the warning during the live test of the extension. If I remember correctly, however, the add-on submission is rejected because of the call to externally_connectable at Manifest. So I believe it is correct to update the online test.

Ideally, FF could just ignore the call in the Manifest and thus allow extension developers not to have to create a dedicated manifest file for FF. But it's just my point of view.

It seems that the discussion of potential security benefits of externally_connectable has side-tracked this discussion.

This feature is better thought of as allowing extensions to communicate with websites without needing permission to "Access your data for all websites", which is what is required today. It won't make extensions more secure, but it will allow them to function with fewer capabilities. Extensions with fewer capabilities pose less risk to users.

Presumably it will also improve extension performance, in that extensions won't need to run a contentscript process anymore just to communicate with websites.

This thread has a lot of discussion about creating trusted communication channels between page and extension, but I think there exists a class of use cases for externally_connectable where that isn't necessary. In particular, there is value in externally_connectable wildcards where an extension can provide service to any website that wants to integrate with it. As @dan mentioned way above, an extension can essentially add features to websites by exposing a new API to any website that integrates with that extension.

This is really useful for things like SSO where the extension would provide authentication services and any webpage would be able to talk to the extension to access those services. For some websites, an extension may be required, while for others the extension could extend page functionality.

Perhaps we could focus on enabling externally_connectable on "*://*/*" only, which should help make it more obvious/clear to dapp developers that there is no guarantee you'll be communicating with any specific page/site/origin, while enabling this feature for API enhancing add-ons.

I don't think there are any security concerns. I've been using this in CHromium for years.

(In reply to Oliver Sauter from comment #38)

hey folks @mozilla

This is such a critical miss for a smooth UX of logging in and sharing data between our own website and the extension for us with memex.garden.
Not adding to the good points about security, but noting that this not being available will force us to abandon our Firefox extension as it breaks critical flows besides also the login/signup flow.

It's super sad we'd like to keep maintaining the extension, but this would require us build and maintain an entirely different (and likely very brittle) solution to sharing data which is not worth the effort for just 7.5% of the browsers.

It would be great if this can be fixed soon.
Thanks!

One workaround for this is to create a WebRTC data channel in the extension and on an arbitrary Web page, exchange SDP between the extension page and the arbitrary Web page to establish a peer-to-peer connection.

(In reply to guest271314 from comment #39)

One workaround for this is to create a WebRTC data channel in the extension and on an arbitrary Web page, exchange SDP between the extension page and the arbitrary Web page to establish a peer-to-peer connection.

Thanks a bunch for throwing in this idea.
It probably makes it possible to build this but it's the kind of different solution that requires a significant amount of new infrastructure for us that we then have to continue maintaining & webRTC has been brittle for us in the past.
Also with the upcoming changes to the Manifest V3, the real-time listening required for a stable webRTC connection would also not work anymore, if I got that right.

Does anyone know how much work it would be to make a PR to Firefox that would enable this?
Seems like its P3 for the team but they may still accept a fix?

(In reply to Oliver Sauter from comment #40)

(In reply to guest271314 from comment #39)

One workaround for this is to create a WebRTC data channel in the extension and on an arbitrary Web page, exchange SDP between the extension page and the arbitrary Web page to establish a peer-to-peer connection.

Thanks a bunch for throwing in this idea.
It probably makes it possible to build this but it's the kind of different solution that requires a significant amount of new infrastructure for us that we then have to continue maintaining & webRTC has been brittle for us in the past.
Also with the upcoming changes to the Manifest V3, the real-time listening required for a stable webRTC connection would also not work anymore, if I got that right.

Does anyone know how much work it would be to make a PR to Firefox that would enable this?
Seems like its P3 for the team but they may still accept a fix?

There no significant infrastructure. WebRTC data channel is exposed on ordinary Web pages. Therefore we can create one on each Web page and extension page (HTML) and figure out a way to create a "signaling server", which could be something like the clipboard, which is system wide. That's how I stream from Nightly to Chromium because Chrome authors refuse to capture monitor devices on Linux, see capture_system_audio_output_from_nightly_stream_to_chromium.md. This is the pattern I use for an extension "signaling server" offscreen-webrtc. And another way to do this using transferable streams sw-transfer-stream.

MV3 doesn't change anything as to real-time listening.

It looks like Firefox authors deliberately don't want to implement this, similar to Firefox authors intentionally not implementing WICG File System Access API, even though nothing in that Draft does anything File API, Drag and Drop, and Clipboard API's can't and don't do.

Similar stuff is happening over on Chrome where Direct Sockets API is gated behind Isolated Web App proposal. However, we can still use a content script to send messages to the extension, though I am using "externally_connectable" to do so right now.

If you need any help just contact me on GitHub or by email or something and I'll see how I can contribute, if I have the time and the requiement is possible, which it should be, because I havn't found a browser "security" implementation I havn't been able to break out of yet.

(In reply to Oliver Sauter from comment #40)

(In reply to guest271314 from comment #39)

One workaround for this is to create a WebRTC data channel in the extension and on an arbitrary Web page, exchange SDP between the extension page and the arbitrary Web page to establish a peer-to-peer connection.

Thanks a bunch for throwing in this idea.
It probably makes it possible to build this but it's the kind of different solution that requires a significant amount of new infrastructure for us that we then have to continue maintaining & webRTC has been brittle for us in the past.
Also with the upcoming changes to the Manifest V3, the real-time listening required for a stable webRTC connection would also not work anymore, if I got that right.

Does anyone know how much work it would be to make a PR to Firefox that would enable this?
Seems like its P3 for the team but they may still accept a fix?

Technically, since Firefox does support Native Messaging we can create a host which is also a web server, if necessary remove Content-Security-Policy, Cross-Origin-Embedder-Policy, Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy headers, append an iframe to an arbitrary Web page, make a fetch() request to POST data to our server, then transfer the response to the Web page from the embedded iframe to facilitate communation between arbitrary Web pages and the extension, see https://github.com/guest271314/webserver-c/tree/quickjs-webserver, https://github.com/guest271314/requestNativeScripts.

Whiteboard: [triaged][chrome] → [triaged][chrome][addons-jira]

To encourage extensions to not request unnecessary host permissions, coupled with the fact that they are currently opt-in in MV3, we are considering the implementation of externally_connectable.

Note that Apple has also implemented externally_connectable under the browser namespace on the web.

Also worth noting that we are in the process of standardizing the existence of the browser namespace, at https://github.com/w3c/webextensions/pull/508 .

To encourage extensions to not request unnecessary host permissions

I don't think that's realistic, nor the purvey of browser source code authors.

Developers will do whatever their requirement is, by any means necessary.

There is Web Bluetooth, Web Share, Web MIDI, Web Serial, Web NFC, WebRTC, WebSocket, WebTransport, Fetch, ServiceWorker, WICG File System Access API (not to be confused with WHATWG File System Standard), IPFS - and those are just the API's specified by specification bodies.

That does not even go proposals that are already implemented in browsers, particularly Chromium and Chrome, e.g., Isolated Web Apps where Direct Sockets (TCPSocket, TCPServerSocket, UDPSocket in the browser) is gated behind - though we can get around those and any other presumptive gates, e.g., FYI: How to connect to a TCP server from an arbitrary Web page in the browser, full working implementation here https://github.com/guest271314/telnet-client.

I would caution trying to steer or groom users to behave a certain way or lower or adjust their expectations to suit browser vendors thinking.

You'll just end up with a bunch of issues, a bunch of workarounds in the wild that eventually break whatever restriction was imposed by the browser, and 7 or 8 years later, wind up implementing the feature because somebody else did and it's pointless to try to gate stuff on the Internet and particularly in a window, which we can communicate with any of anyway using executeScript().

Firefox implementing "externally_connectable" is basically catching up to Chrome - years later. Because Apple did it?

What you can do that is modern is the implementation. Instead of using IPC (JSON essentially) as Chromium-based browsers do (for Native Messaging, too JSON is used) utilize Transferable Streams for the data exchange https://bugs.chromium.org/p/chromium/issues/detail?id=1214621. Then Firefox will be at par with the current technology.

Implementation note: When the implementation commences, we need to make sure that window.browser in content scripts return the extension API of the content script (instead of the web page), to minimize the chance of regressions. For the context of that, see https://github.com/w3c/webextensions/pull/508#issuecomment-1912200989

(In reply to guest271314 from comment #46)

Firefox implementing "externally_connectable" is basically catching up to Chrome - years later. Because Apple did it?

The reason for reconsidering the implementation is in comment 45: when the bug was filed, any host permissions granted by extensions were granted at install time. Since then, we have added UI for users to toggle permissions. This means that extension authors have to account for the possibility that the requested host permissions have not been granted. The externally_connectable feature enables websites to communicate with extensions even if the host permission has been denied.

What you can do that is modern is the implementation. Instead of using IPC (JSON essentially) as Chromium-based browsers do (for Native Messaging, too JSON is used) utilize Transferable Streams for the data exchange https://bugs.chromium.org/p/chromium/issues/detail?id=1214621.

Firefox's extension messaging APIs are not limited to JSON, but use serializable cloning.
I'm in favor of more efficient messaging mechanisms, but that is independent of the work in this bug. In case you're curious, my thoughts (and responses from other browser vendors) are at: https://github.com/w3c/webextensions/issues/293#issuecomment-1310203017

(In reply to Rob Wu [:robwu] from comment #45)
It's very exciting to hear that Firefox is considering implementing externally_connectable. Thank you!

(In reply to guest271314 from comment #46)

To encourage extensions to not request unnecessary host permissions

I don't think that's realistic, nor the purvey of browser source code authors.

Speaking on behalf of MetaMask (GitHub), we want externally_connectable precisely because it would allow us minimize the authority that we need from the browser. See also the Chromium team citing this logic as part of their motivation for fixing this bug.

Firefox's extension messaging APIs are not limited to JSON, but use serializable cloning.

Yes, I think I remember that. It's cumbersome to install extensions on Firefox. I've done it, but I normally write Chromium extensions.

I'm in favor of more efficient messaging mechanisms, but that is independent of the work in this bug. In case you're curious, my thoughts (and responses from other browser vendors) are at: https://github.com/w3c/webextensions/issues/293#issuecomment-1310203017

That's funny. Somebody filed that feature request over on GoogleChrome/samples re similar requirement Sample: Transfer a blob from a background context to a page #766 before they banned me, and I created at least two (2) working examples of how to do that. Naturally the GoogleChrome folks closed the PR I filed https://github.com/GoogleChrome/chrome-extensions-samples/pull/782.

Here's some working code for you (the last time I checked)

I have transferred data between contexts, windows, extensions and windows various ways.

Above we use Transferable Streams, keeping the MV3 extension ServiceWorker active indefinitely; and use WebRTC Data Channels between an offscreen document and the Web page. The WebRTC Data Channel option is particularly useful for communicating cross-origin, cross-browser, and to and from any context or device on the planet that supports WebRTC - including between tabs on the same machine.

Basically I can communicate between any tab or window, no matter CSP, CORP, COEP, etc.

Good luck!

Speaking on behalf of MetaMask (GitHub), we want externally_connectable precisely because it would allow us minimize the authority that we need from the browser. See also the Chromium team citing this logic as part of their motivation for fixing this bug.

What should also be possible is setting Web pages Native Messaging hosts can communicate directly to, just like externally_connectable. Instead of having to route through externally_connectable, when we are using extension code at all to run arbitrary native applications and shell scripts from the browser, where we could use Web API's https://github.com/guest271314/fs.

While "externally_connectable" is useful, and it should be possible to officially set any URL or <all_urls>, frankly I have not had that issue in a while because I (we can) dynamically modify manifest.json to set any arbitrary Web page as "externally_connectable", e.g.,

https://github.com/guest271314/captureSystemAudio/blob/10908bd48f08018a338aa046a26429189bbcdc7b/native_messaging/capture_system_audio/background.js#L384C1-L406C4

  if (!tab.url.startsWith('chrome:')) {
    const url = new URL(tab.url);
    const { matches } = chrome.runtime.getManifest().externally_connectable;
    const match = matches.find((m) => new URL(m).hostname === url.hostname);
    if (match === undefined) {
      const request = await fetch('manifest.json');
      const json = await request.json();
      json.externally_connectable.matches = [
        ...new Set([`${url.origin}/*`, ...json.externally_connectable.matches]),
      ];
      const message = await chrome.runtime.sendNativeMessage(
        'set_externally_connectable',
        json
      );
      console.log(message);
      dir = await navigator.storage.getDirectory();
      let handle = await dir.getFileHandle('tabId.txt', { create: true });
      await new Blob([tab.id]).stream().pipeTo(await handle.createWritable());
      handle = await dir.getFileHandle('update.txt', { create: true });
      await new Blob([]).stream().pipeTo(await handle.createWritable());
      chrome.runtime.reload();
    }
  }

https://github.com/guest271314/captureSystemAudio/blob/10908bd48f08018a338aa046a26429189bbcdc7b/native_messaging/capture_system_audio/set_externally_connectable.js#L12C1-L28C2

function sendMessage(message) {
  const fd = std.open('manifest.json', 'rw+');
  const json = JSON.parse(String.fromCharCode(...message));
  fd.puts(JSON.stringify(json, null, 2));
  fd.close();
  const header = Uint32Array.from(
    {
      length: 4,
    },
    (_, index) => (message.length >> (index * 8)) & 0xff
  );
  const output = new Uint8Array(header.length + message.length);
  output.set(header, 0);
  output.set(message, 4);
  std.out.write(output.buffer, 0, output.length);
  std.out.flush();
}

(In reply to Rob Wu [:robwu] from comment #47)

Firefox's extension messaging APIs are not limited to JSON, but use serializable cloning.
I'm in favor of more efficient messaging mechanisms, but that is independent of the work in this bug. In case you're curious, my thoughts (and responses from other browser vendors) are at: https://github.com/w3c/webextensions/issues/293#issuecomment-1310203017

An aside, you might, at your leisure, re-advise your colleagues over in GoogleChrome/samples world that their Native Messaging example is still broken https://github.com/GoogleChrome/chrome-extensions-samples/issues/805, in more ways than one (I think we went over this in a MDN/Firefox bug that there is no /usr/bin/python on some Linux systems now, python3 is there). We can use the Python code from the updated MDN Web Docs example. W3C banned me, too, so I can't comment over there.

You need to log in before you can comment on or make changes to this bug.