w3c / webappsec-fetch-metadata

Fetch Metadata
https://w3c.github.io/webappsec-fetch-metadata/
Other
75 stars 28 forks source link

Folding Sec-Same-Origin into Fetch Metadata? #43

Open deian opened 5 years ago

deian commented 5 years ago

Following up on TPAC conversation. It would be great to not have multiple mechanisms for similar enough problems. The doc @spinda and I put together describing the high-level idea behind our approach is here. We will make the Firefox fork, data sets and experiments available with our academic paper write up (though we can share here/elsewhere before we finish writing up the paper).

Here are the difference I think we need from Fetch metadata to encompass Sec-Same-Origin:

  1. The equivalent of Sec-Frame-Same-Origin. From our doc: With each navigation request and potential-navigation-or-subresource request, a Sec-Frame-Same-Origin header is sent to inform the server if the requested page will be contained within a cross-origin frame. Specifically, if there is no containing frame, or the URI of the page that owns the containing frame is same-origin with the request’s destination URI, then the UA will send Sec-Frame-Same-Origin: 1; else, Sec-Frame-Same-Origin: 0.

  2. The equivalent of the discloseorigin attribute and Sec-Frame-Origin header. When this attribute is set on forms and frames, we disclose the precise origin (e.g., https://w3c.org) with the Origin and Sec-Frame-Origin headers respectively (overriding Referrer Policy). If we modify the Referrer and Fetch spec to additionally apply the referrerpolicy attribute to frames and forms elements we won't need the disloseorigin attribute (I need to read the spec closer but I assume the attribute is more specific so overrides the header). We still need the Sec-Frame-Origin header though.

We'll also need to make sure that our approach to computing the SameOrigin or not (the origin classification algorithm in the document) align.

@arturjanc @lweichselbaum @spinda

arturjanc commented 5 years ago

Thanks, Deian! A couple of thoughts about this, hopefully we'll get to talk about this more during the upcoming WebAppSec TPAC follow-up discussion tomorrow.

Re: (2), the idea of having an opt-in mechanism that would attach the Origin header to chosen requests seems reasonable, I attempted to explore this direction a bit in https://github.com/whatwg/fetch/issues/700#issuecomment-382762249. I see this as an extension to the current model that allows the mechanism to be deployed in more places without exempting parts of the application which get cross-site requests from a small set of trusted domains from enforcement (you could reject cross-site requests unless they have an allowlisted Origin). I wonder if we could do this separately as a Referrer Policy feature.

Re: (1), I was wondering what security properties you're hoping to get by knowing that the embedding happens in cross-site frame. That is, compared to a request with Sec-Fetch-Mode: nested-navigate and Sec-Fetch-Site: cross-site, what does sending Sec-Frame-Same-Origin: 0 help us protect against?

deian commented 5 years ago

Re: (2) ... I wonder if we could do this separately as a Referrer Policy feature.

Sounds pretty reasonable.

Re: (1) .. what security properties you're hoping to get ... compared to a request with Sec-Fetch-Mode: nested-navigate and Sec-Fetch-Site: cross-site ...

Knowing whether or not the iframe is being embedded in a same-origin page is useful in pretty much same scenarios as X-Frame-Option/frame-ancestors. The key difference is that it allows you to make more informed decisions server-side.

Here are two scenarios from our look at WordPress:

  1. WP has a comment-composition page that is embedded in iframes. This page can be embedded in both same- and cross-origin pages. When the form within the iframe is submitted, they check server-side if the iframe is in a same-origin page (via a token protocol) and if not they automatically escape the comment even if the user has HTML commenting enabled. They're trying to avoid clickjacking-based code-injection attacks.

  2. The WP upgrade page embeds an iframe that ultimately displays a progress bar of the install. But, server-side the request for the iframe page begins an installation. A cross-origin page can embed this page just as easily, so they again use tokens to prevent this.

In both cases X-Frame-Options is not really a great option: it's not about showing the response, we don't even want to handle the request.

Admittedly I think what I wrote about was not even strict enough: we want to only send 1 if none of the ancestors are tainted. We may also want to send this for other kinds of requests (e.g., subrequests within framed pages), but that may be overkill.

annevk commented 5 years ago

@arturjanc if you have A embedding B and B', and B navigates B', would that be same-origin with the current processing model? I.e., information about your parent can be different from information about who navigates you. (It's not clear that only parent is enough though, it seems you would want something similar to ancestorOrigins, though with the patch that Mozilla proposed to leak less information.)

arturjanc commented 4 years ago

@arturjanc if you have A embedding B and B', and B navigates B', would that be same-origin with the current processing model?

Yes, it should; that's what Chrome does currently. In general, the value in Sec-Fetch-Site should be the entity which controls the URL of the request, so for navigations it should be the origin which navigates you (or, more precisely, the least trusted party that can control the URL so that e.g. any cross-site redirect will taint the request so that it's always cross-site).

Knowing whether or not the iframe is being embedded in a same-origin page is useful in pretty much same scenarios as X-Frame-Option/frame-ancestors. The key difference is that it allows you to make more informed decisions server-side.

Thanks for the interesting examples, @deian! I agree there are applications which would benefit from this (at Google we have embeddable YouTube and Google Docs widgets that have different behavior when they're iframed), but there are a couple of things to consider:

So in this case I'm not entirely sure if the extra information would be as broadly useful as the other values in the Sec-Same-Origin and Fetch Metadata proposals.

deian commented 4 years ago

@arturjanc: The location.ancestorOrigins approach works for the first use case but not the second. You really need to make the decision server-side.

RE broadly useful/common enough: I guess I see WP as a big enough deal to get right (since like a quarter of sites on the web are WP, but maybe this statistic is changing), but I'd be curious if you have data on behavior staying the same wether or not a page is framed same-origin or not.

deian commented 4 years ago

The other case I'd like to make for this is almost as a compliment to X-Frame-Options but enforceable server-side. XFO is great, but should really be a defense-in-depth backup strategy; we shouldn't be replying with pages that contain sensitive data and trust the browser to not leak them. Particularly because different browsers have different underlying isolation mechanisms and different mitigation for transient execution attacks.

arturjanc commented 4 years ago

In the second case, couldn't the server just look at Sec-Fetch-Site on the request for the frame and only initiate a state-changing action if it's same-origin, i.e. use the generic anti-CSRF protection of Fetch Metadata? Allowing server-side enforcement complementary to X-Frame-Options is definitely a design goal of Fetch Metadata (this is why we have mode=nested-navigate), I'm not sure how anything higher up in the frame chain than the direct embedder is relevant in this situation assuming application-wide logic that checks Sec-Fetch-Site.

I don't have solid data on when the behavior of a framed document differs depending on whether the embedder is same-origin, but, anecdotally, framebusting is the main one, and that's a legacy use case for which we now have better platform mechanisms. Basically, unlike the other values, which the application cannot get without browser support, the "is the same-origin request I'm making sent from a frame that's being embedded cross-site" bit is already something that the application can get from the platform in places where it's useful.

I certainly don't mind annotating requests with more security-relevant metadata, I guess I'm just not yet sold on the value of having the browser attach this particular bit.

deian commented 4 years ago

Woop, you're right. I was thinking of a different case. @spinda maybe you remember? Or I'll edit this when I remember.

But, actually I am not really convinced that your proposed solution to the first case via location.ancestorOrigins is the right way to do it. The application will be more complicated and needs to again rely on client-side enforcement to do something pretty simple vs. single server-side middleware. The need to use JavaScript means that sites that want to work with Tor need to again do hacky token-based checking.

If we're going to try to keep the amount of metadata minimal it seems like that ship has sailed already (e.g., do we really need to say that something is an `image when you can have JavaScript do the checking?).

arturjanc commented 4 years ago

But, actually I am not really convinced that your proposed solution to the first case via location.ancestorOrigins is the right way to do it. The application will be more complicated and needs to again rely on client-side enforcement to do something pretty simple vs. single server-side middleware.

My intuition is that it's more commonplace to execute such logic before the form is submitted because you'd want to indicate to the user that some actions are unavailable in a cross-site frame. For example, if we don't want to allow a Google Docs widget to enable certain actions when it's framed cross-site (e.g. changing sharing settings), we would remove/disable parts of the UI instead of allowing the user to execute the action and then rejecting it on the server.

A declarative mechanism that avers that all ancestors of a frame which sends an HTTP request are same-origin also seems fairly constraining as it wouldn't allow e.g. trusting embedders from certain allowlisted domains, which you could do with ancestorOrigins.

The need to use JavaScript means that sites that want to work with Tor need to again do hacky token-based checking.

That's a good point, this would either need JavaScript or require the server to add something like

<input name="is_embedder_same_origin"
   value="{{req.headers['Sec-Fetch-Site'] == 'same-origin'}}">

to forms in framed documents that should have a different behavior depending on whether their document is framed same-origin or not.

I don't much about Tor Browser, but FWIW I thought they block third-party cookies, so requests from an iframe embedded cross-site wouldn't be authenticated?

If we're going to try to keep the amount of metadata minimal it seems like that ship has sailed already

I think it's more about how broadly useful the headers are for defending against cross-site attacks. My guess is that most applications would benefit from protections against CSRF, timing attacks, and more generally allowing their resources to be loaded by cross-site documents -- this is the core of both the Sec-Same-Origin and Fetch Metadata proposals.

Having an embeddable widget loaded by other sites and changing its behavior based on whether the embedder is same-origin seems to be a fairly infrequent use case, and it's already possible without adding features to the platform (either by reading ancestorOrigins or by walking the ancestor chain and detecting any non-same-origin window).

If we have signals from developers that the alternatives are insufficient, and that they would use this bit, I think it we should consider adding it. Otherwise, my guess is that we'll get most of the security value of these mechanisms with Sec-Fetch-Site and Sec-Fetch-Mode, at least for the initial version of the spec.