Feature Request: extension permission request's can be triggered by webpages

413 views
Skip to first unread message

Andy Richardson

unread,
May 5, 2020, 10:16:19 AM5/5/20
to Chromium Extensions
About
Hey folks, this is a proposal to address some of the security challenges the chrome review process is currently having and the limitations associated with the activeTab permission.

What's the problem
The activeTab permission spec is great for allowing the user to dynamically approve permission requests on a case-by-case basis rather than at extension installation time. 

This comes with a pretty major limitation however which is that these permission requests can only be triggered by a user interaction. This is unusual considering that most other "permission" specs (such as the w3 permissions spec or Android permissions spec) allow for ad-hoc requests from the requesting party. 

If user-triggers are a blocking limitation, the alternative is to request the ability to run a content script on all URLs. This is obviously quite a security concern for users and could be considered as the equivalent of giving an extension "root" permissions to the browser.

Proposal
A browser spec where the running site can trigger extension permission prompts similar to how it would request camera access.

"This page want's to grant the following permissions for [extension name]..." 

Similarly to how the w3 permissions spec works, once a permission had been granted for a URL, it would no longer need to be requested.

Example
Here's a psuedo example of what this might look like running on a webpage.

chrome.permissions.request({ 
  extension: '<some_extension_id>', 
  permissions: ['activeTab'] 
});

This would subsequently trigger a prompt to the user in the following cases:
  • they have the extension with the associated id installed
  • the extension supports webpage based permission requests
Assuming the user approves, the extension's content script would be injected and any specific permissions requested would be granted.

Who benefits
  • Users - a more explicit and controllable way of granting access to extensions
  • Developers - a familiar API and faster review periods
  • Google - fewer manual reviews
Further info
This solves a widespread problem (particularly in the devtools space) where:
  • The target URL cannot be determined upfront
  • The target webpage is "extension aware" (i.e. there is already extension-specific code running on that page which could trigger a permission request)

Here are a few example projects which could benefit from this addition:

Simeon Vincent

unread,
May 5, 2020, 9:59:01 PM5/5/20
to Chromium Extensions
Some thoughts while I settle in this morning (plus some evening addendums). Please note that these are my personal (somewhat sleepy) opinions and do not reflect those of the Chrome team or Chromium contributors.

The problem as stated sounds like it may be confusing the activeTab permission and chrome.permissions.request() method. To be clear, activeTab  grants temporary host permissions for the current site in response to a user invoking the extension (browser action click, context menu selection, triggering a keyboard shortcut, etc.) while permissions.request() requires a user gesture (generic user-initiated web event) to request a (host/optional) permission specified in your manifest.json. 

The proposal mentions that this change would require Google to perform fewer manual reviews and (thereby presumably) accelerate the review process for developers. I don't think that necessarily follows. Host permissions are extremely powerful and dangerous – the reason we don't have to scrutinise activeTab as closely as broad host permissions requests is because activeTab is temporary, scoped to the current site, and granted in context in response to a user's invocation. These 3 design constraints significantly limit the potential harm of this permission. This proposal would change the meaning of activeTab to grant persistent access to a site effectively loosening the design constraints and broadening the number of extensions we'd have to closely scrutinize during review.

This proposal actually sounds much closer to having <all_urls> or *://*/* in your optional_permissions list (or host_permissions in MV3) and using chrome.permissions.request() to request persistent access to the current host. If we pivot the proposal from activeTab to requesting persistent host permissions via permissions.request(), I think the proposal more closely reflects the current permissions model and has a clearer capability request: allow sites to initiate a host permissions request for their host in a given extension.

I think you may be able to achieve something close to what you described using the current messaging APIs and permissions.request(). Extensions can specify what sites or URL patterns can communicate with them via the externally_connectable property in manifest.json. In this case you'd probably want to set "externally_connectable": { "matches": ["*://*/*"] }. With that pattern, arbitrary websites can message the extension via chrome.runtime.sendMessage(extensionId, messagePayload, function callback() { … }) and extensions can listen for these messages via chrome.runtime.onMessageExternal.addListener(function messageHandler(message, sender, sendResponse) { … }). With that all in place (plus tabs in permissions and *://*/* in optional_permissions) an arbitrary website could bind to a user-initiated event, use that event handler to post a message to the extension, and the extension could request access to the host specified in sender.url

One final note I'd like to touch on before wrapping up is the Chrome's permissions model for extensions vs. the W3C and Android models. I haven't dug into the design constraints with Chrome engineers, but I suspect that extensions and sites/apps are different because the constraints of these two broad categories are different. Both websites and apps are self-contained: if they provide a bad experience, you can simply close them to avoid a abusive UX patterns. Unfortunately, the same isn't true of extensions. Since extensions modify the browser – essentially the system environment – there's no directly analogous way to prevent a bad actor from spamming users until they grant the desired permission. The user could quit Chrome, but then they can't remove the offending extension whereas an app user can always uninstall the app or modify it's access using system controls. Since extensions modify the environment rather than an experience within the environment, I suspect that the user gesture requirement was introduced as a natural throttling mechanism. Again, this is supposition on my part, so take it with a heavy dose of salt.

Cheers,

Simeon - @dotproto
Extensions Developer Advocate

Andy Richardson

unread,
May 6, 2020, 5:20:17 AM5/6/20
to Chromium Extensions
Hey Simeon, thanks for getting involved - really appreciate your feedback & suggestions!


the reason we don't have to scrutinise activeTab as closely as broad host permissions requests is because activeTab is temporary, scoped to the current site, and granted in context in response to a user's invocation

 This is the primary goal of this proposal - apologies I'm not too hot on the API. The core idea here is, if a site can invoke a request for an extension to run, we would keep this criteria:
  • temporary access (arguably less important if the user explicitly approves - similar to web APIs where granting is only required once)
  • scoped to current site
  • granted in context in response to a user's i̶n̶v̶o̶c̶a̶t̶i̶o̶n̶  approval
Extensions can specify what sites or URL patterns can communicate with them via the externally_connectable property in manifest.json. In this case you'd probably want to set "externally_connectable": { "matches": ["*://*/*"] }. With that pattern, arbitrary websites can message the extension via chrome.runtime.sendMessage(extensionId, messagePayload, function callback() { … }) and extensions can listen for these messages via chrome.runtime.onMessageExternal.addListener(function messageHandler(message, sender, sendResponse) { … }). With that all in place (plus tabs in permissions and *://*/* in optional_permissions) an arbitrary website could bind to a user-initiated event, use that event handler to post a message to the extension, and the extension could request access to the host specified in sender.url

This sounds like it might solve the issue - I'm assuming this will have a similar result whereby:
  • no permissions are requested on installation 
  • the site communicates to the extension that it is "compatible" in some manner
  • the extension explicitly requests permission to run on the url
I'll give this a shot today and let you know how it goes!

there's no directly analogous way to prevent a bad actor from spamming users until they grant the desired permission.

I can totally see what you mean by this. I'm not so sure about the browser permissions spec but I've seen alternate ways of restricting this on Android with the introduction of a "never allow" option (I appreciate something like this would require big changes). 

Again, a huge thanks for your feedback! I'll give externally_connectable a try today.

Andy

Andy Richardson

unread,
May 6, 2020, 9:19:34 AM5/6/20
to Chromium Extensions
Quick update! I tried out your suggestion but unfortunately the externally_connectable.matches attribute requires a minimum specificity of a  second level domain.

Andy Richardson

unread,
May 14, 2020, 8:55:02 AM5/14/20
to Chromium Extensions
Trying to keep this discussion alive. Here are a few mentions from other developers about this issue:

Geoffrey De Belie

unread,
May 14, 2020, 2:24:00 PM5/14/20
to Chromium Extensions
Please keep in mind that there are a lot of extensions that request <all_urls>, but for a legitimate use. Some functionalities can only be implemented as a content script and are meant for generic use. Single letter shortcuts is an example, overlays (as a kind of window) another.


Even Tab Center Reborn needs it:

Dev tool extensions could benefit from this, but calling chrome.permissions.request in a web page creates an awareness you don't want to have (in my opinion). The extension itself should detect whether a page is running React / Redux / Angular, not the other way around. Otherwise you limit the amount of extensions a web framework can have, because web pages are not aware of third party extensions.

For me, creating a new permission type "detectGenerator" reading the meta value for "generator" would be the best solution. It could be implemented like https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/detectLanguage is implemented (or without additional permission, just like detectLanguage).

Requesting permissions should then be handled automatically by the extension, with the user to be able to "refuse and remember", "refuse this time" and "allow", just like microphone and webcam permissions are handled.

Pseudo code (web page = page script):
<meta name="generator" value="VueJS 2.4.0"/>

Pseudo code (background script)
browser.tabs.onUpdated.addListener((tabId, changeInfo, tabInfo) => {
  browser.tabs.detectGenerator(tabId).then(url, generator) => {
console.log(generator); // For example, VueJS 2.4.0
};
}

Geoffrey De Belie

unread,
May 14, 2020, 2:31:27 PM5/14/20
to Chromium Extensions
Full pseudo code example

Pseudo code (web page = page script):
<meta name="generator" value="VueJS 2.4.0"/>

Pseudo code (background script)
browser.tabs.onUpdated.addListener((tabId, changeInfo, tabInfo) =>
{
  browser.tabs.detectGenerator(tabId).then(url, generator) => {
    console.log(generator); // For example, VueJS 2.4.0
    if(generator.startsWith("VueJS")){
      browser.permissions.request({ permissions: ['activeTab'] });
    }
  };
}

Andy Richardson

unread,
May 18, 2020, 6:51:02 AM5/18/20
to Chromium Extensions
Hey Geoffrey,

I totally agree, there are valid use cases for <all_urls> and this proposal isn't a catch-all solution - it's more intended to dramatically reduce the number of extensions on the marketplace that currently have no other option than the <all_urls> permission. 

Here are extensions which arguably do need <all_urls> permission as they run on every URL.

Ad-blockers
Manipulate elements and network interactions on every url
CSS/Feature Tweakers
Manipulate/inject visual content on every url

Here are extensions which do not need to run on all sites but only sites within a specific scope. Despite this, they require <all_urls> permission because this scoping can't be declared in the current browser extension spec.

Dev-tooling
Communicate with extension specific JS code and present info to the user. In this case, the JS code could in theory be the party requesting permission for the extension to start its content script.
    Others
    Still looking into this - but here's a quick example.
    • Wordpress Scanner - only runs on sites where Wordpress is present (inferable by script names / metadata maybe)


    Dev tool extensions could benefit from this, but calling chrome.permissions.request in a web page creates an awareness you don't want to have (in my opinion). The extension itself should detect whether a page is running React / Redux / Angular, not the other way around. Otherwise you limit the amount of extensions a web framework can have, because web pages are not aware of third party extensions.

    The more I've been thinking about this, the more I've started to agree. Technically, I'm unsure how this would be implemented but upfront permissions for a narrowly scoped eval would be ideal. There would be some pretty heavy limitations with this approach however. When would the eval script be called? What if the page uses virtual routing? What if the metadata/content the extension is looking for isn't guaranteed on initial page load.

    The generator idea seems like a nice idea for adding new scoping. I suspect there are still a fair amount of scopes that couldn't be covered by this however.
    ---

    Side note, just briefly looking at your example of Event Tab Center Reborn, I suspect it would work fine with just the tabs permission.

    Andy Richardson

    unread,
    Jun 23, 2020, 5:52:43 AM6/23/20
    to Chromium Extensions
    Hey all, quick update on where I stand with this.

    I've spent a lot of time thinking about this problem and, as Geoffrey has identified, there are some holes in this suggestion.

    I've put together a post summarising all the things I've learned about the review process so far and also going into detail about some of the problems we've experienced in relation to the current spec and its limitations.

    Any feedback would be greatly appreciated! I still think there are flaws with the current process but hopefully feedback like this will help identify where change is needed.
    Reply all
    Reply to author
    Forward
    0 new messages