New API proposal: omnibox spoofing for chrome-extension: URLs

168 views
Skip to first unread message

Rob Wu

unread,
Jan 10, 2015, 11:58:18 AM1/10/15
to apps-dev, Peter Kasting, deano...@gmail.com, Justin Schuh

While this API may sound dangerous at first (and one of the do-nots at the CRX API Security Checklist), it is not, because this API is restricted to extension tabs and require explicit permission for the hosts that can be spoofed. Extensions cannot do anything evil that cannot be done with content scripts today.

[I have CC'd Peter and Justin because of their input on an old issue regarding the displayed URL for extensions at https://crbug.com/72021]

Kind regards,
 Rob
 https://robwu.nl

Daniel Herr

unread,
Jan 11, 2015, 9:41:04 PM1/11/15
to apps...@chromium.org, pkas...@chromium.org, deano...@gmail.com, jsc...@chromium.org
It would be nice if the omnibox could be set to display, for instance, the extension name. The real url could be displayed when the user focuses the omnibox.

Justin Schuh

unread,
Jan 12, 2015, 10:41:00 AM1/12/15
to Daniel Herr, Mustafa Emre Acer, apps-dev, Peter Kasting, deano...@gmail.com
You'll want to loop in meacer@ from the security team, rather than me.

Rob Wu

unread,
Feb 9, 2015, 4:41:28 PM2/9/15
to apps-dev, Daniel Herr, Mustafa Emre Acer, Peter Kasting, Dean Oemcke, Benjamin Kalman
So far, I've only received a few comments about features that are not relevant to the core of the proposal (origin chip, url_handlers, colorful omnibox URLs).

Can I proceed with the implementation of this API, or are there any blocking issues?

Kind regards,
 Rob
 https://robwu.nl

To unsubscribe from this group and stop receiving emails from it, send an email to apps-dev+u...@chromium.org.

Benjamin Kalman

unread,
Feb 9, 2015, 5:00:47 PM2/9/15
to Rob Wu, Charlie Reis, apps-dev, Daniel Herr, Mustafa Emre Acer, Peter Kasting, Dean Oemcke
Charlie really needs to have a look at this. Other than I'm ok with this API as a replacement for content scripts (i.e. require host permissions).

Charlie Reis

unread,
Feb 9, 2015, 5:04:36 PM2/9/15
to Benjamin Kalman, Rob Wu, apps-dev, Daniel Herr, Mustafa Emre Acer, Peter Kasting, Dean Oemcke, Nasko Oskov
Can you point me to the proposal for this?  (Apologies if I missed it before.)

Granting the power to spoof the omnibox can be extremely dangerous, so I'd like to understand what steps this proposal takes to make it safe.

Charlie

Rob Wu

unread,
Feb 9, 2015, 5:06:52 PM2/9/15
to Charlie Reis, Benjamin Kalman, Rob Wu, apps-dev, Daniel Herr, Mustafa Emre Acer, Peter Kasting, Dean Oemcke, Nasko Oskov

Benjamin Kalman

unread,
Feb 9, 2015, 5:08:24 PM2/9/15
to Rob Wu, Charlie Reis, apps-dev, Daniel Herr, Mustafa Emre Acer, Peter Kasting, Dean Oemcke, Nasko Oskov
Charlie, the catalyst for this was this bug, which you may remember.

Charlie Reis

unread,
Feb 10, 2015, 2:08:39 PM2/10/15
to Benjamin Kalman, Rob Wu, apps-dev, Daniel Herr, Mustafa Emre Acer, Peter Kasting, Dean Oemcke, Nasko Oskov
Ah yes, I remember that bug.  I think we can probably make this work, if we implement it safely.  A few observations and minor discussion points:

1) From a threat model perspective, this proposed API isn't granting the extension any more power than it had before with content scripts.  So that seems reasonable to me, as long as we don't inadvertently make it work it cases that content scripts don't.

2) In terms of implementation, it's very important that these overridden URLs are not made visible to the rest of Chrome, which might try to make security decisions based on them.  Other code in both the renderer process and browser process should continue to see the URL as the chrome-extension:// URL, not as what's shown to the user.  This means we should not use replaceState in Blink, nor the VirtualURL methods on NavigationEntry (since those are exposed to other code from methods like WebContents::GetLastCommittedURL).  Instead, this should probably be strictly a display override in the omnibox code.

3) We can't allow this API to set the URL to chrome:// URLs.  Are content scripts already blocked for chrome:// URLs?

4) Like content scripts, this would require having host permissions for the URLs displayed.  kalman@ made a comment about moving away from host permissions in future extension APIs.  Will adding this API make future plans be more difficult, or is there a way forward?

Hope that helps!  Please do get a security review on the implementation (from myself or Nasko).

Thanks,
Charlie


Peter Kasting

unread,
Feb 10, 2015, 4:32:05 PM2/10/15
to Charlie Reis, Benjamin Kalman, Rob Wu, apps-dev, Daniel Herr, Mustafa Emre Acer, Dean Oemcke, Nasko Oskov
On Tue, Feb 10, 2015 at 11:08 AM, Charlie Reis <cr...@chromium.org> wrote:
Other code in both the renderer process and browser process should continue to see the URL as the chrome-extension:// URL, not as what's shown to the user.  This means we should not use replaceState in Blink, nor the VirtualURL methods on NavigationEntry (since those are exposed to other code from methods like WebContents::GetLastCommittedURL).  Instead, this should probably be strictly a display override in the omnibox code.

It's going to be a mess if the omnibox can't use the virtual URL to decide this.  "Virtual URL" seemed to me to be exactly what this called for -- the "visible" URL rather than the "actual" URL.  Isn't it wrong for code to use the virtual URL to make security decisions and the like?

Also, if the omnibox displays some URL <x>, then if the user does something like hit enter on that string, we're going to try to load that directly, not some secret underlying URL.  In other words, if an extension can rewrite the visible URL to look like "foo.com", user edits to the address bar are going to load foo.com, not some extension URL.  If that's not acceptable, this whole API isn't really going to fly, because there's no easy way around this.

PK

Joel Weinberger

unread,
Feb 10, 2015, 4:47:06 PM2/10/15
to Peter Kasting, Charlie Reis, Benjamin Kalman, Rob Wu, apps-dev, Daniel Herr, Mustafa Emre Acer, Dean Oemcke, Nasko Oskov
I've said this at other times, but it seems that, ultimately, the feature we actually want is to allow extensions to install Service Workers for sites they have host permissions for. This would, I think, resolve a lot of Peter's comments and other thoughts on this thread, as well as a variety of other extension use cases. And I think it would avoid a lot of this internal tooling and complexity that we're discussing.

On the other hand, I've heard, though, that it would be a very large amount of work to get that done, and it would probably introduce a world of its own complexity, so perhaps it's not really an option.
--Joel

Rob Wu

unread,
Feb 10, 2015, 5:22:46 PM2/10/15
to Peter Kasting, Charlie Reis, Benjamin Kalman, Rob Wu, apps-dev, Daniel Herr, Mustafa Emre Acer, Dean Oemcke, Nasko Oskov
2015-02-10 20:08 GMT+01:00 Charlie Reis <cr...@chromium.org>:
3) We can't allow this API to set the URL to chrome:// URLs.  Are content scripts already blocked for chrome:// URLs?

Yes, unless the --extensions-on-chrome-urls flag is enabled AND the extension has requested the <all_urls> permission.
 
4) Like content scripts, this would require having host permissions for the URLs displayed.  kalman@ made a comment about moving away from host permissions in future extension APIs.  Will adding this API make future plans be more difficult, or is there a way forward?

The API is designed with the assumption that the extension can already access/modify the site, so spoofing the URL is safe.
I'm not certain how extensions are going to operate when host permissions get removed, but the API could still function even if host permissions did not exist, e.g. in the following scenarios, the affected URLs can safely be spoofed, because evil extensions could already have compromised the site's security at that point:
1. When an extension has a host permission via activeTab in any of the tabs.
2. When an extension has modified a request (e.g. via the webRequest API).
 
2015-02-10 22:32 GMT+01:00 Peter Kasting <pkas...@chromium.org>:
On Tue, Feb 10, 2015 at 11:08 AM, Charlie Reis <cr...@chromium.org> wrote:
Other code in both the renderer process and browser process should continue to see the URL as the chrome-extension:// URL, not as what's shown to the user.  This means we should not use replaceState in Blink, nor the VirtualURL methods on NavigationEntry (since those are exposed to other code from methods like WebContents::GetLastCommittedURL).  Instead, this should probably be strictly a display override in the omnibox code.

It's going to be a mess if the omnibox can't use the virtual URL to decide this.  "Virtual URL" seemed to me to be exactly what this called for -- the "visible" URL rather than the "actual" URL.  Isn't it wrong for code to use the virtual URL to make security decisions and the like?

VirtualURL seems like the most suitable way to implement this API without reinventing the wheel. Things like bookmarks will then also work out of the box. Security decisions should be based on URL, not VirtualURL, since URL is the true (internal) representation of the resource, right?

(that having said, why does WebContentsImpl::GetURL return VirtualURL instead of URL?)

 
Also, if the omnibox displays some URL <x>, then if the user does something like hit enter on that string, we're going to try to load that directly, not some secret underlying URL.  In other words, if an extension can rewrite the visible URL to look like "foo.com", user edits to the address bar are going to load foo.com, not some extension URL.  If that's not acceptable, this whole API isn't really going to fly, because there's no easy way around this.

That is perfect, and exactly the reason why I want such an API. The URL in the omnibox is supposed to be a representation of the displayed content, and if an extension uses this API to spoof the omnibox, then the extension should try to make sure that the displayed content is indeed a representation of the content.

Benjamin Kalman

unread,
Feb 10, 2015, 5:32:04 PM2/10/15
to Joel Weinberger, Peter Kasting, Charlie Reis, Rob Wu, apps-dev, Daniel Herr, Mustafa Emre Acer, Dean Oemcke, Nasko Oskov
On Tue, Feb 10, 2015 at 1:47 PM, Joel Weinberger <j...@chromium.org> wrote:
I've said this at other times, but it seems that, ultimately, the feature we actually want is to allow extensions to install Service Workers for sites they have host permissions for. This would, I think, resolve a lot of Peter's comments and other thoughts on this thread, as well as a variety of other extension use cases. And I think it would avoid a lot of this internal tooling and complexity that we're discussing.

On the other hand, I've heard, though, that it would be a very large amount of work to get that done, and it would probably introduce a world of its own complexity, so perhaps it's not really an option.
--Joel


There are other good reasons to invest in the realm of "service workers + extensions" but it can mean at least 3 different things. In any case, it doesn't address the case when you don't actually have a request to service in the first place (i.e. anything you want to do after the page loads, like taking a copy of the tab and closing the original).
 

On Tue Feb 10 2015 at 1:32:06 PM Peter Kasting <pkas...@chromium.org> wrote:
On Tue, Feb 10, 2015 at 11:08 AM, Charlie Reis <cr...@chromium.org> wrote:
Other code in both the renderer process and browser process should continue to see the URL as the chrome-extension:// URL, not as what's shown to the user.  This means we should not use replaceState in Blink, nor the VirtualURL methods on NavigationEntry (since those are exposed to other code from methods like WebContents::GetLastCommittedURL).  Instead, this should probably be strictly a display override in the omnibox code.

It's going to be a mess if the omnibox can't use the virtual URL to decide this.  "Virtual URL" seemed to me to be exactly what this called for -- the "visible" URL rather than the "actual" URL.  Isn't it wrong for code to use the virtual URL to make security decisions and the like?


There are some tricky things here as well.

For example, what should the session entries look like? You'd want the session entries to be synced, e.g. if I spoof chrome-extension://abc/page.html to show http://example.com then I'd want tab restore, whether local or cross device, to restore http://example.com not chrome-extension://abc/page.html.

At the content layer you'd want it to look like chrome-extension://abc/page.html though, like Charlie says. You wouldn't want an extension with host permissions for http://example.com to be able to access what turns out to be a chrome-extension://abc origin.

There must be a whole bunch more corner cases to get this right. If I zoom one of these spoofed http://example.com pages does the origin-based zooming effect http://example.com, or does it effect the chrome-extension://aaa origin? (presumably the latter if this is done at the content layer, but the former would be more useful).

The complexity is making me uneasy.
 
Also, if the omnibox displays some URL <x>, then if the user does something like hit enter on that string, we're going to try to load that directly, not some secret underlying URL.  In other words, if an extension can rewrite the visible URL to look like "foo.com", user edits to the address bar are going to load foo.com, not some extension URL.  If that's not acceptable, this whole API isn't really going to fly, because there's no easy way around this.


I think that's a similar problem to session restore, and in the use cases I've seen, the behavior you describe is what's desired.
 
PK

Mustafa Emre Acer

unread,
Feb 10, 2015, 5:40:00 PM2/10/15
to Benjamin Kalman, Joel Weinberger, Peter Kasting, Charlie Reis, Rob Wu, apps-dev, Daniel Herr, Dean Oemcke, Nasko Oskov
On top of code complexity, we'll now need to account for spoofed urls when making decisions about omnibox, permissions, warnings or any other security UX with this API.

For example, should a spoofed https://www.google.com still show the lock icon? Should an EV domain still show the EV indicator? What happens if the extension spoofs a url that's a broken SSL or is already in SafeBrowsing blacklist? How do we explain to the user that the lack of an SSL indicator is because of a spoofed url?

Rob Wu

unread,
Feb 10, 2015, 5:48:52 PM2/10/15
to Mustafa Emre Acer, Benjamin Kalman, Joel Weinberger, Peter Kasting, Charlie Reis, Rob Wu, apps-dev, Daniel Herr, Dean Oemcke, Nasko Oskov
Security concerns about the spoofed URL roughly falls in two categories:
1. The fact that the extension inherits the trust from the spoofed URL ("Omnibox says google.com, so it is probably google.com").
2. The possibility that code expects the tab to not be very privileged, and allows others to access it, while in reality the tab is a privileged chrome-extension:-URL ("example.com? This site cannot do anything, let's give X access to the tab").

Issue 1 is not a problem at all, because if an extension can use this API, then it can also insert a content script on that host and do whatever it want while the omnibox displays a non-extension URL.

Issue 2 needs to be accounted for, at the implementation stage every corner case needs to be explored and tested, and fixed if needed.

Kind regards,
 Rob
 https://robwu.nl

Charlie Reis

unread,
Feb 13, 2015, 7:46:01 PM2/13/15
to Rob Wu, Mustafa Emre Acer, Benjamin Kalman, Joel Weinberger, Peter Kasting, apps-dev, Daniel Herr, Dean Oemcke, Nasko Oskov
Sorry for the much delayed reply, as I haven't had much time recently.  Several replies inline.

On Tue, Feb 10, 2015 at 2:48 PM, Rob Wu <r...@robwu.nl> wrote:
Security concerns about the spoofed URL roughly falls in two categories:
1. The fact that the extension inherits the trust from the spoofed URL ("Omnibox says google.com, so it is probably google.com").
2. The possibility that code expects the tab to not be very privileged, and allows others to access it, while in reality the tab is a privileged chrome-extension:-URL ("example.com? This site cannot do anything, let's give X access to the tab").

Issue 1 is not a problem at all, because if an extension can use this API, then it can also insert a content script on that host and do whatever it want while the omnibox displays a non-extension URL.

Issue 2 needs to be accounted for, at the implementation stage every corner case needs to be explored and tested, and fixed if needed.

I tend to agree with this.  Issue 1 isn't a problem if we treat this as equivalent to content script powers.  Issue 2 concerns me.
 

Kind regards,
 Rob
 https://robwu.nl

On Tue, Feb 10, 2015 at 1:32 PM, Peter Kasting <pkas...@chromium.org> wrote:
On Tue, Feb 10, 2015 at 11:08 AM, Charlie Reis <cr...@chromium.org> wrote:
Other code in both the renderer process and browser process should continue to see the URL as the chrome-extension:// URL, not as what's shown to the user.  This means we should not use replaceState in Blink, nor the VirtualURL methods on NavigationEntry (since those are exposed to other code from methods like WebContents::GetLastCommittedURL).  Instead, this should probably be strictly a display override in the omnibox code.

It's going to be a mess if the omnibox can't use the virtual URL to decide this.  "Virtual URL" seemed to me to be exactly what this called for -- the "visible" URL rather than the "actual" URL.  Isn't it wrong for code to use the virtual URL to make security decisions and the like?

Not according to the comments on NavigationEntry::GetVirtualURL:
  // The virtual URL, when nonempty, will override the actual URL of the page
  // when we display it to the user. This allows us to have nice and friendly
  // URLs that the user sees for things like about: URLs, but actually feed
  // the renderer a data URL that results in the content loading.

We don't want about: or chrome: URLs treated like data: URLs when making security decisions.

It's possible that comment is stale, but I imagine a lot of the call sites would be based on that, and there do seem to be a lot of places using the virtual URL.

On Tue, Feb 10, 2015 at 2:22 PM, Rob Wu <r...@robwu.nl> wrote:
2015-02-10 22:32 GMT+01:00 Peter Kasting <pkas...@chromium.org>:
On Tue, Feb 10, 2015 at 11:08 AM, Charlie Reis <cr...@chromium.org> wrote:
Other code in both the renderer process and browser process should continue to see the URL as the chrome-extension:// URL, not as what's shown to the user.  This means we should not use replaceState in Blink, nor the VirtualURL methods on NavigationEntry (since those are exposed to other code from methods like WebContents::GetLastCommittedURL).  Instead, this should probably be strictly a display override in the omnibox code.

It's going to be a mess if the omnibox can't use the virtual URL to decide this.  "Virtual URL" seemed to me to be exactly what this called for -- the "visible" URL rather than the "actual" URL.  Isn't it wrong for code to use the virtual URL to make security decisions and the like?

VirtualURL seems like the most suitable way to implement this API without reinventing the wheel. Things like bookmarks will then also work out of the box. Security decisions should be based on URL, not VirtualURL, since URL is the true (internal) representation of the resource, right?

(that having said, why does WebContentsImpl::GetURL return VirtualURL instead of URL?)

Given how the code currently works, I disagree with using virtual URLs (as noted above).  I've only recently tried to understand the virtual URL code in any depth, and I'm not sure how it ended up as it did.  Without an overhaul, I would not consider it safe for this use case.


 
Also, if the omnibox displays some URL <x>, then if the user does something like hit enter on that string, we're going to try to load that directly, not some secret underlying URL.  In other words, if an extension can rewrite the visible URL to look like "foo.com", user edits to the address bar are going to load foo.com, not some extension URL.  If that's not acceptable, this whole API isn't really going to fly, because there's no easy way around this.

That is perfect, and exactly the reason why I want such an API. The URL in the omnibox is supposed to be a representation of the displayed content, and if an extension uses this API to spoof the omnibox, then the extension should try to make sure that the displayed content is indeed a representation of the content.
 
I'm surprised by this.  Generally reload or session restore puts you back in the state you were in, but this would end up returning the actual URL's content and not the extension's content.


On Tue, Feb 10, 2015 at 2:31 PM, Benjamin Kalman <kal...@chromium.org> wrote:
The complexity is making me uneasy.

+1

Charlie


Benjamin Kalman

unread,
Feb 13, 2015, 7:56:54 PM2/13/15
to Charlie Reis, Rob Wu, Mustafa Emre Acer, Joel Weinberger, Peter Kasting, apps-dev, Daniel Herr, Dean Oemcke, Nasko Oskov

On Fri, Feb 13, 2015 at 4:45 PM, Charlie Reis <cr...@chromium.org> wrote:
I'm surprised by this.  Generally reload or session restore puts you back in the state you were in, but this would end up returning the actual URL's content and not the extension's content.

I would guess the theory is that an Extension wants to pretend it's some other URL, because it wants to preserve the URL. Of the two use cases I've seen, one solves this because one assumes the next time the URL is loaded the extension will intercept it again, and the other solves it because restoring the original page is the point of the extension.

Tiago Freitas

unread,
Mar 8, 2015, 3:34:55 PM3/8/15
to apps...@chromium.org, cr...@chromium.org, r...@robwu.nl, mea...@chromium.org, j...@chromium.org, pkas...@chromium.org, danielst...@gmail.com, deano...@gmail.com, na...@chromium.org
In the case of the thegreatsuspender extension, the point is for the url to be replaced, and not to be intercepted by the extension again.

This is because there are no extensions for mobile devices, and when chrome open tabs / history is synced to a mobile device, the original website url should be used and not the extension url, which doesn't open on mobile.

Related to this issue:
Reply all
Reply to author
Forward
0 new messages