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

How should iframe navigations work? #38

Closed
raymeskhoury opened this issue Oct 20, 2016 · 17 comments
Closed

How should iframe navigations work? #38

raymeskhoury opened this issue Oct 20, 2016 · 17 comments
Milestone

Comments

@raymeskhoury
Copy link

What should we do in the case where an iframe navigates itself to an origin other than the one it is initialized to?

One case to consider here is, the embedder says enable="geolocation" for an iframe initialized to "good-site.com" but then the iframe is navigated to "bad-site.com". We don't want geolocation to be enabled.

A potential solution is to ignore enable attributes after a cross-origin navigation has occurred, unless the enable attribute is modified and then the iframe reloaded.

We also need to take care to apply the parent frames header policy correctly when an iframe is navigated to a new origin.

@clelland @mikewest @igrigorik @ojanvafai

@igrigorik
Copy link
Member

If we grant X to iframe, that iframe can embed any other iframe and grant X to its descendant...

IMO redirects are fair game here. If we grant X to an origin/frame and that frame redirects, then it is, in effect, delegating that (full/same) access to the redirect destination.

@raymeskhoury
Copy link
Author

I wasn't thinking about redirects specifically. Consider the case where I embed "google.com" and give it geolocation access. The user does a search in the iframe and clicks on a search result which navigates to a random website, bad.com. In this case, neither the top level page, nor the embedded page has given any indication that bad.com should be given access.

We had the same principle of revoking access for permission delegation. I think the same applies here. Thoughts?

@igrigorik
Copy link
Member

FWIW, I think redirects are very common.. if we revoke the feature on redirects, I think that'd be a very painful experience for a lot of pages.

Re, navigations: this smells like a CSP + FP policy combination.

<iframe enable="geolocation" src="https://foo.com"
        csp="navigate-to https://foo.com https://bar.com">

The discussion for navigate-to is w3c/webappsec-csp#125, and rumor has it, embedded CSP implementation may be underway in Chrome. /cc @mikewest

@raymeskhoury
Copy link
Author

FWIW, I think redirects are very common.. if we revoke the feature on redirects, I think that'd be a very painful experience for a lot of pages.

Hmm - I understand the concern now. I still don't feel very comfortable with this from a security standpoint though, at least in the general case of one site navigating to another, for example navigating by setting window.location or by a user clicking a link. There is no indication that the site trusts the feature to be delegated to the site being navigated to. Furthermore, the original origin is now out of the picture and is no longer an active actor in the scenario. It no longer holds any responsibility for the actions of the navigated site. It's different in that sense from the frame embedding a site and actively delegating access.

I think I'd feel more comfortable if ads that needed it added in an extra iframe in their chain where all the redirects happened and they could delegate access to all origins they embed.

I'd be curious of the view of some security folks too :)

@mikewest
Copy link
Member

The discussion for navigate-to is w3c/webappsec-csp#125, and rumor has it, embedded CSP implementation may be underway in Chrome. /cc @mikewest

The page can already limit the things its frames can navigate to by setting frame-src whatever.com on its own policy. Embedded enforcement isn't necessary unless you want to govern the places to which an embedded frame can open a new window (but it is ~half done behind a flag in Canary).

One case to consider here is, the embedder says enable="geolocation" for an iframe initialized to "good-site.com" but then the iframe is navigated to "bad-site.com". We don't want geolocation to be enabled.

This is made worse by the fact that frames are navigable cross-origin. That is, if I have a handle to your window (via window.open or window.opener or by framing you or by you framing me), then I can cause your frames to navigate: otherWindow.frames[0].contentWindow.location = 'https://evil.com/'.

It would be unfortunate if that gave folks a trivial way of gaining permissions that had been granted to someone else.

@clelland
Copy link
Collaborator

We decided on the syntax enable="feature" as a kind of shorthand for iframes, declaring that it would mean "enable the feature for this frame", and not thinking that the origin embedded in that frame could change.

If that's problematic, (and it seems that it is,) then we could change the interpretation of the shorthand, to explictly mean: "enable the feature in this frame for the initial origin embedded in it".

We could also change the syntax, to require developers to write something like

<iframe src="https://benevolent.com/" enable="geolocation:benevolent.com" >

or even require something like the JSON used for the full policy:

<iframe src="https://benevolent.com/" enable='{"geolocation": "https://benevolent.com"}' >

If a site really wanted to allow a feature to all origins potentially embedded in the frame, then it could

<iframe src="http://www.dmoz.org/" enable="geolocation:*" >

to allow that.

@raymeskhoury
Copy link
Author

@clelland that's a good point :) I think the simplest thing, most secure and most common case is to delegate the feature only to the initial origin. I feel like that's good syntax to keep.

We could add additional syntax to specify which origins specifically as you suggested - I think we would want to make it a bit scarier because I think the guarantees are much less obvious.

Just brainstorming: would a separate attribute feature-policy="..." which allows specifying a full policy to override the header, but only for that iframe be a better approach? This would be scarier/more complicated but give full control. The enable/disable syntax would just be sugar for specifying the full policy. Is there precedent for specifying json in an attribute?

@igrigorik
Copy link
Member

Can we nail down the threat model we're trying to address and how and why existing methods are insufficient? I'm still dubious of the solutions we're driving towards here..

  1. enable inherently offers much weaker security guarantees because it allows the application to specify the FP policy at runtime (as long as the header and meta policy allows it). As such, if the top level page is XSS'ed.. the script can delegate permission to whomever it wants. The only way to avoid that is to eliminate enable.
  2. Once you've granted permission to a frame you're extending full trust to that frame to delegate that permission in whatever way they like -- e.g. they can inject a frame and delegate it further down the chain, they can redirect themselves to somewhere else -- same difference, I think.

As @mikewest pointed out, if the top-level frame wants to protect itself against subframe's navigating themselves, it can use frame-src.. For sub-iframes, I believe the same frame-src via embedded enforcement applies?

How or why is the above not sufficient? I'd rather layer the various mechanisms, instead of trying to reinvent the wheel multiple times.

@clelland that's a good point :) I think the simplest thing, most secure and most common case is to delegate the feature only to the initial origin. I feel like that's good syntax to keep.

Not sure if it's simple.. But I'm pretty confident it's a non-starter for most interesting use cases -- e.g. ads, which often bounce through a series of redirects.

@raymeskhoury
Copy link
Author

raymeskhoury commented Oct 28, 2016

I think what we're really discussing is what the scope of an "enable" attribute should be by default, i.e. to which origins in the iframe should it apply (@clelland stated it well above :).

@igrigorik it seems like your preference is that by default an enable attribute should apply to any origin that gets loaded in the iframe to which it applies. If the embedder wants to prevent this from happening they need to restrict navigations by using another mechanism, e.g. frame-src. Is that accurate?

I'm not sure this is the best approach. It seems too easy to accidentally give geolocation to any origin that gets loaded in the iframe, and these origins might not be trusted by anyone. It means that in order to achieve least privilege, a developer first has to open up a big hole with "enable" and then subsequently lock it down with extra navigation restrictions. I think that people will be shooting themselves in the foot because they don't realize how broadly they are delegating access and they forget/never realize to lock it down afterward (see the examples above). That's why it seems preferable (from a security standpoint) to go the other way around: by default "enable" is scoped to the initial origin and if a developer wants it apply more broadly they can explicitly specify that.

Not sure if it's simple.. But I'm pretty confident it's a non-starter for most interesting use cases -- e.g. ads, which often bounce through a series of redirects.

I agree :) I think we need to find a solution that addresses this too. I'm just not sure that having enable apply to all origins that get loaded (by default) is the best approach.

@igrigorik
Copy link
Member

@igrigorik it seems like your preference is that by default an enable attribute should apply to any origin that gets loaded in the iframe to which it applies.

I wouldn't say it's a preference, as much as.. a coherency and layering question: if sandbox whitelist grants privilege to all origins of the frame, then FP should -- I think -- follow the same model. That way, at least we have a single and simple path for developers: if you need to constrain the granted privileges use frame-src, etc. Mixing frame-src with special enable syntax for FP, with.. (insert whatever other mechanisms we dream up later).. is unmanageable.

I understand your concerns though, and I agree with them. I'm just arguing for a single mechanism to govern all this, instead of ad-hoc approaches in each mechanism.

@raymeskhoury
Copy link
Author

It's a good point that sandbox doesn't revoke access on navigations, but I think there is an important difference. Sandbox is all about locking things down, rather than giving extra privilege. It's more like the "disable" attribute than the "enable" attribute. I think it would be ok (preferable) for the "disable" attribute to apply to all origins that get loaded in the iframe, just not enable. With FP we're considering delegation of powerful features. We need to be very careful that trust has been explicitly expressed by someone before giving access. I don't think that happens when enable applies to all origins in a frame.

Going back to the original example I had, I don't think this will be acceptable from a security standpoint, even with the fact that navigations can be restricted:

Consider the case where I embed "google.com" and give it geolocation access. The user does a search in the iframe and clicks on a search result which navigates to a random website, bad.com. In this case, neither the top level page, nor the embedded page has given any indication that bad.com should be given access.

@igrigorik
Copy link
Member

Well, there is a reason why we don't allow google.com to be iframed (x-frame-options:SAMEORIGIN).. :-) The destination can protect itself by x-frame-options, the embedder can protect itself via CSP frame-src, right?

That said, what's the concrete proposal we're evaluating at this point? As I said earlier, I think we're solving this at the wrong layer, but I'm happy to be convinced otherwise.

@raymeskhoury
Copy link
Author

Well, there is a reason why we don't allow google.com to be iframed (x-frame-options:SAMEORIGIN).. :-) The destination can protect itself by x-frame-options, the embedder can protect itself via CSP frame-src, right?

I don't think the reason we prevent google.com from being iframed is related to this issue though? It's easy to use other sites as an example, like bing.com that don't have x-frame-options. I don't think this isn't about the embedder or the destination protecting itself - rather the embedder being very specific about who they delegate access to. Having a default that grants access very broadly is a recipe for the embedder giving access to someone unintentionally.

The options we were discussing were to have "enable" apply only to the origin in "src" by default and having a way to broaden that access if the embedder really wants to, for example by having an additional attribute which allows specifying a full policy to apply to that frame. There are lots of variations of that idea that I'm open to though.

@igrigorik
Copy link
Member

Discussed on VC today, the proposal is:

<iframe href=foo.com featurepolicy='{"usermedia": ["*"]}'>
<iframe enable=usermedia href=foo.com>

The former enables "full policy" syntax, which also allows the embedder to delegate permission to all origins (e.g. to deal with unknown redirects, etc). The latter (shorthand) enable attribute is, on the other hand, simpler and safer in that it implicitly only delegates permission to the origin specified by frame element's href attribute value.

@clelland
Copy link
Collaborator

clelland commented Nov 9, 2016

The shorthand is equivalent to:

<iframe src=foo.com featurepolicy='{"usermedia": ["http://foo.com"]}'>

but is shorter, and hopefully less error-prone
Also implcit in this is that the disable attribute has been replaced with featurepolicy='{"usermedia": []}'

We still need to resolve what happens if both attributes are specified -- whether for the same, or for different features, and whether changing the src attribute on the iframe element (as opposed to having the iframe navigate itself) should change that implicit origin.

@igrigorik
Copy link
Member

Updated proposal in https://docs.google.com/document/d/1k0Ua-ZWlM_PsFCFdLMa8kaVTo32PeNZ4G7FFHqpFx4E/edit#heading=h.t3vfp0hdaogx.

Anything else we need to address on this one, or can we close it and iterate in the explainer?

@igrigorik igrigorik added this to the v1 milestone Nov 23, 2016
@clelland
Copy link
Collaborator

Closing this -- the end result of this is that the policy applied by the parent document to its child frames can specify a list of origins, (a single origin in the limit) or allow all origins loaded in the frame to use the feature ("*").

The iframe allow attribute takes a list of feature names and creates a container-policy from them which applies to the document loaded in that frame (see https://wicg.github.io/feature-policy/#container-policies). That policy will contain, for each named feature, the single origin from the iframe element's src attribute, so that if the framed document navigates away, the feature will not be allowed.

For compatibility, the allowfullscreen and allowpaymentrequest attributes behave similarly, but generate a container policy that matches all origins. This allows the framed content to navigate to a new origin which will still retain the ability to use the feature, matching the behaviour of those attributes today. (https://wicg.github.io/feature-policy/#legacy-attributes)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants