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

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

Open
deian opened this issue Sep 17, 2019 · 9 comments
Open

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

deian opened this issue Sep 17, 2019 · 9 comments
Labels
enhancement New feature or request

Comments

@deian
Copy link
Member

deian commented Sep 17, 2019

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
Copy link
Contributor

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 whatwg/fetch#700 (comment). 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
Copy link
Member Author

deian commented Sep 21, 2019

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
Copy link
Member

annevk commented Sep 21, 2019

@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
Copy link
Contributor

@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:

  • This seems to be a less common use case than controlling framing via X-Frame-Options or frame-ancestors because it applies only in situations when you have documents that need to be embedded cross-site, but where the author wants the behavior to differ depending on the embedder. From experience, most embedded documents (even those that use frame-ancestors for fine-grained control over embedding) don't change behavior based on whether they are iframed same-origin or not.
  • Applications already have the information that lets them protect themselves from these attacks in location.ancestorOrigins and by inspecting Sec-Fetch-Mode to see if the nested navigation was initiated cross-site (pending discussion in Naming for new items in mode. #45). Instead of adding logic on the server to reject requests if they come from a frame embedded cross-site, developers can add logic on the client to not make the request at all if location.ancestorOrigins indicates the document is in a cross-site iframe (and inspect Sec-Fetch-Site on the server as usual to protect against straight up CSRF). You could even do this with a Service Worker to annotate all outgoing same-origin requests from the frame, and you'd have more control because you could directly inspect embedding origins in places where you need to enforce a custom allowlist.

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
Copy link
Member Author

deian commented Sep 27, 2019

@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
Copy link
Member Author

deian commented Sep 27, 2019

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
Copy link
Contributor

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
Copy link
Member Author

deian commented Sep 28, 2019

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
Copy link
Contributor

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.

@mikewest mikewest added the enhancement New feature or request label Jul 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants