w3c / webappsec-secure-contexts

WebAppSec Secure Contexts
https://w3c.github.io/webappsec-secure-contexts/
Other
33 stars 38 forks source link

Should opener be taken into consideration when determining if a context is secure? #42

Closed jakearchibald closed 6 years ago

jakearchibald commented 7 years ago

Ok, I know this is going to sound like heresy, but I'm thinking a window opened by a non-secure context via window.open or <a target="_blank"> should still be considered a secure context.

I agree that an HTTPS iframe embedded in an HTTP page should not be considered secure, since that gives you an invisible and automatic way of creating a channel between the two, but window.open and <a target="_blank"> are visible, and both require a user interaction.

Imagine http://example.com contains:

<iframe src="https://secure-proxy.example.com/proxy">
<a href="https://secure-proxy.example.com/worker" target="_blank" rel="noopener">Click me</a>

The iframe is non-secure, but the opened tab is secure. The secure opened tab can perform secure actions, and send it to the non-secure iframe via any origin storage, BroadcastChannel, or in a hacky way SharedWorker (since its security is decided by the document which creates it).

If we drop the parts of the spec that take opener into account when deciding if the context is secure users get a more consistent experience when following links, as their seemingly top-level contexts are consistently able to access powerful features.

The alternative, to combat the above, is to add isSecureContext to the origin identifier. Meaning otherwise secure contexts opened via target="_blank" are a different origin to contexts opened to the same URL via target="_blank" rel="noopener". But that exacerbates the weird user experience.

jakearchibald commented 7 years ago

Additionally, imagine https://example.com

<a href="http://example.com/worker" target="_blank">Click me</a>

If the link above is clicked, http://example.com now has a WindowProxy reference to https://example.com, but https://example.com remains a secure context unless the state of a context's security is mutable.

jakearchibald commented 7 years ago

As @mikewest says, .opener is a misfeature. We could look at how much of the web would break if it were disabled when crossing schemes.

jakearchibald commented 7 years ago

It'd be great to see numbers for "window.opener used from an HTTPS page to an HTTP page" - could we deprecate in this case?

mikewest commented 7 years ago

Arg, sorry I missed this when you filed it. Thanks for the ping.

So. Heresy or not, I'm also not thrilled with the opener restrictions, but I'm not sure that throwing them out completely is the right thing to do. I think I agree that in some cases it ends up being more confusing than helpful to taint a popup based on the way it was opened.

The general idea behind the opener restriction is to close down communication channels between secure origins and non-secure origins in order to prevent feature use in one context from leaking into another. As you note (and as described in https://w3c.github.io/webappsec-secure-contexts/#isolation), a number of communication side-channels exist for same-origin content that we haven't addressed via this restriction. Those side channels are hampered by the fact that no direct DOM access exists between the contexts, but they exist as long as we don't use the context's "secure" status to influence the origin (which I'm very reluctant to consider).

That said, the same side- and direct-channels exist for <iframe> elements. You've suggested that you're ok with those restrictions because you recognize the risk, and further that <iframe> is invisible and doesn't require a user gesture. I'm not sure that either of those limitations actually makes a significant difference. Getting a click to work with is trivial, and visibility only matters insofar as users would consider it odd that a window was opened. I have great faith in the ability of folks who want to work around the restrictions we're putting in place to build reasonable-feeling UI that looks like it's doing one thing, while actually exfiltrating data. More involved, yes, impossible, hardly.

Still, I'm totally sympathetic to the notion that we shouldn't punish innocent bystander sites to which a user legitimately wants to navigate. Since direct DOM access worries me much more than side-channel access, perhaps we could take the opener context's origin and content into consideration more than we do today. A pretty ugly strawman would be something like "If there's a non-secure view onto the openee's origin (or an origin which has opted into same-originness via document.domain) in the opener's frame tree, the openee is non-secure. Otherwise evaluate it as a standalone."? I'm not sure that actually helps though, and it's even more complex and confusing than the current setup.

I'd suggest that we can pursue a three-pronged approach here:

  1. Give pages the ability to protect themselves from the leakage opener implies, which we're poking at in https://github.com/w3c/webappsec/issues/517 and https://github.com/w3c/webappsec/issues/139.
  2. Reexamine the default behavior of opener. I think that turning it off when transitioning schemes is appealing, but I know that a number of federated identity providers poke window.opener in order to complete a sign-in process (as an example, all of the federated options on https://discourse.wicg.io/ rely on popups). I'm reluctant to break those for non-secure origins; I'm all for getting folks over to HTTPS, but unilaterally breaking folks' authentication mechanisms seems like quite a stick to wield.
  3. Evaluate weakening the opener restriction in Secure Contexts, as discussed above.

(/cc @annevk @bifurcation @hillbrad @jwatt)

annevk commented 7 years ago

1, in particular https://github.com/w3c/webappsec/issues/517 seems good (basically a signal that you want to become a new top-level browsing context rather than auxiliary, also useful if you want independent process benefits and such).

@bifurcation can be pinged through @rlbmoz.

mikewest commented 7 years ago

@annevk: Yeah. A stub of that is in CSP3: https://w3c.github.io/webappsec-csp/#directive-disown-opener

travisleithead commented 7 years ago

I tend to agree with the ordered approach above. The more often a window can be legitimately disowned (via cross-origin navigation, or one of the opt-in options mentioned) the better, and allows for more opportunities for strong isolation in the UA (e.g., process isolation hinted at by Anne above). If these options help to re-set the "inherited" (in)secure context flag for that new window, then, that's just further incentive for site authors to apply it.

So, I don't have issue with the way the secure context flag is extended to opener windows today.

jakearchibald commented 7 years ago

The difference between a window & an iframe is the origin that's displayed in the URL bar. It seems fine to limit iframes because the user can be unaware that the framed origin is executing code.

In regards to postmessaging, could this have been tackled as part of MIX? As in, signal a drop in security if a mixed communication happens, and allow CSP to prevent mixed communication?

https://github.com/w3c/webappsec/issues/517 will solve this for geolocation & such, but it won't solve it for service worker. We need to know if the site's SW can handle the navigation fetch, and at that point we don't know if the site is going to drop opener.

We may drop opener automatically for SW-controlled scopes, although I've been reluctant to introduce changes in page behaviour resulting from an empty service worker. It could be opt-in on the activate event.

jakearchibald commented 7 years ago

I think that turning it off when transitioning schemes is appealing, but I know that a number of federated identity providers poke window.opener in order to complete a sign-in process

Do they generally use window.open or target="_blank" too. Might be easier to deprecate opener on the latter when it's cross-scheme.

mikewest commented 7 years ago

w3c/webappsec#517 will solve this for geolocation & such, but it won't solve it for service worker.

Origin policy?

We may drop opener automatically for SW-controlled scopes

I would be pretty happy with this resolution.

jakearchibald commented 7 years ago

An origin policy would work here also. But I think we'll auto-disown opener if opener is a non-secure context. Happy for this to close if you are.

mikewest commented 7 years ago

Sure. I'll leave the bit as "at risk" in the spec just in case we can come up with something better, but happy to close this for the moment.

mikewest commented 7 years ago

Actually, let's leave this open, just for visibility.

dveditz commented 7 years ago

This is causing us angst. We've been using the "is a secure context" state to warn about entering passwords on insecure pages. As a result we have confused users wondering why some of their https:// pages are warning them that submitting their password is insecure. It looks secure when they look at the url bar. Very hard to convince them that "the page might pass information insecurely via its opener" makes the page insecure. The site might be storing passwords in plain text in a world-readable file once they are securely transmitted.

Of course we could decide passwords are a special case and determine secureness differently, but if we're having trouble explaining this insecure-secure-site case to users maybe developers aren't going to get it either. If anything I'd rather just nullify opener by default if we think this is a significant case we need to stop, and make sites specify explicit rel=insecureopener to get it back (and then treat the spawned context as insecure).

mikewest commented 7 years ago

@dveditz: We don't tie mixed content checks to the context being "secure", only to it being delivered over a secure transport. The password check you're talking about in Firefox seems much more similar to a mixed content check than to a "should this API be available at all" check. shrug

jwatt commented 7 years ago

For the record, as of Firefox 52 we added a privileged-code-only isSecureContextIfOpenerIgnored property to compliment isSecureContext, and the code that checks for passwords before allowing form submission uses the former. Offhand I'm not even sure why we care to check the security of anything other than the page containing the form to be honest, but I guess the more we can get away with locking things down and pushing the Web to HTTPS the better.

mnoorenberghe commented 7 years ago

As it is now I think everything in Gecko should use isSecureContextIfOpenerIgnored instead of isSecureContext for the reasons explained before that there is currently nothing web developers can do to avoid an insecure opener from third-party sites in browsers. We just used isSecureContextIfOpenerIgnored for geolocation in bug 1072859 too since we noticed that's basically what Chrome and Safari did. From what I could tell Chrome doesn't implement the at-risk opener part of the spec in their secure context method. It seems like we need to remove the opener check from the spec due to it breaking websites until we have an deployed solution (e.g. the CSP ones) in browsers allowing authors to prevent the issue.

mikewest commented 6 years ago

I screwed this up in Chrome, sorry for both the angst and the churn. I've dropped the section from the spec, which brings it into line with everyone's implementations. My apologies. :(

annevk commented 6 years ago

As @jonathanKingston pointed out and also illustrated by the comment from @jwatt above, Firefox does actually implement this. We have https://bugzilla.mozilla.org/show_bug.cgi?id=1410364 to align with the changed standard. It would be good if there were tests.

bzbarsky commented 6 years ago

We have tests; @jwatt added them when we implemented in Firefox. See http://w3c-test.org/secure-contexts/

It looks like @mikewest already updated those tests in https://github.com/w3c/web-platform-tests/commit/dd83f891b80aa9b05e5fe35d1680e53c6a435b07