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

Why is Sec-Fetch-Site based on the full URL redirect chain? #28

Closed
anforowicz opened this issue May 15, 2019 · 7 comments
Closed

Why is Sec-Fetch-Site based on the full URL redirect chain? #28

anforowicz opened this issue May 15, 2019 · 7 comments

Comments

@anforowicz
Copy link

anforowicz commented May 15, 2019

Hello,

I wonder if https://mikewest.github.io/sec-metadata/#sec-fetch-site-header could clarify (in a non-normative comment maybe) why the algorithm considers "each url in r’s url list". An alternative would be to only consider the current/last URL in the redirect chain.

Notes:

  • The alternative is easier to implement - see https://crbug.com/956146 (OTOH, inspecting the full chain is also implementable, just results in more complex code)
  • In many cases an attacker can bypass this aspect of the algorithm (i.e. ignore earlier portion of the redirect chain) by directly asking for the final URL. I guess this bypass is not possible if the redirect is to a secret URL that is only known to the redirecting server and is unknown to the client/attacker.
  • I can't think of a scenario where the full-chain mitigates an attack (I understand that this is a weak argument and may simply mean that I am not very imaginative):
    • Example1: attacker.com makes a request to attacker.com/blah which redirects to victim.com/boom. In this case victim.com sees Sec-Fetch-Site set to either cross-site (full chain) or cross-site.
    • Example2: victim.com makes a request to attacker.com/blah which redirects to victim.com/boom. In this case victim.com sees Sec-Fetch-Site set to either cross-site (full chain) or same-origin. But then, it victim.com can be coerced into making a request to attacker.com/blah, then it probably can also be coerced into making a direct request to victim.com/boom.
@arturjanc
Copy link
Contributor

Basically, open redirects. The example that (I believe) you're missing above is "attacker.com makes a request to victim.com/redir?url=//victim.com/secret which redirects to victim.com/secret". In this case we can't send the final request as same-origin because this would make it seem trusted while the response would end up in an attacker-controlled context. And open redirects are a common feature on the web, so we don't want them to render Fetch Metadata ineffective.

In many cases an attacker can bypass this aspect of the algorithm (i.e. ignore earlier portion of the redirect chain) by directly asking for the final URL.

I'm not entirely sure I understand this point -- an attacker can make a request to the final URL, but that direct request will be cross-site (the least trusted value) and the server will likely choose to reject it. So the attacker doesn't gain anything in this case.

@anforowicz
Copy link
Author

Sorry, but I am still not getting it :-/. Please see my replies below.

Basically, open redirects. The example that (I believe) you're missing above is "attacker.com makes a request to victim.com/redir?url=//victim.com/secret which redirects to victim.com/secret". In this case we can't send the final request as same-origin

The algorithm that is described at https://mikewest.github.io/sec-metadata/#sec-fetch-site-header and implemented in Chrome compares the origin of the initiator (i.e. attacker.com) with the origin of the target (i.e. victim.com/redir... or victim.com/secret). The result of the comparison would be cross-site for both destinations in your example 1) the final destination and 2) the intermediate destination.

In other words, in your example, the final request will not be same-origin regardless of whether the algorithm looks only at the final destination or at the full redirect chain.

I think you might be (incorrectly I think) saying that the final request will use same-origin value because it will compare origin of victim.com/redir with the origin of victim.com/secret? This comparison will not be made, because victim.com/redir is not the initiator of the request.

I'm not entirely sure I understand this point -- an attacker can make a request to the final URL, but that direct request will be cross-site (the least trusted value) and the server will likely choose to reject it. So the attacker doesn't gain anything in this case.

Exactly! For the attacker to gain something, the earlier destination has to be less attacker-friendly than the final destination:

  • The most attacker-friendly final destination would be same-origin (initiator=victim.com, final_destination=victim.com). To make earlier redirects less attacker-friendly, we can have initiator=victim.com -> attacker.com/redir -> victim.com/secret. This is my example2.

BTW: Maybe it is okay to say that Sec-Fetch-Site considers the full chain as a defense-in-depth (currently there are no known attacks that it prevents)? Or is there an attack that I am missing?

@arturjanc
Copy link
Contributor

Ah, yes, I understood your proposal to "consider the current/last URL in the redirect chain" as only comparing the URLs between the last two hops of the redirect (i.e. if we have a chain A -> B -> C I thought you meant B -> C but now I understand you meant A -> C).

I think the primary reason to consider the full chain is the case where an application loads any external resources; for example if my application fetches an <img> or <iframe> from a server which I don't fully control, the owner of that server could redirect the request back to an arbitrary endpoint on my site. The response will be included in the context of my own application, but in some cases this sufficient for an attacker (e.g. it would allow CSRF, or reveal the size of the encrypted response to a network-level attacker.)

A similar concern applies in cases where an application allows sanitized attacker-controlled markup, such as a webmail client which renders a safe subset of HTML. The sanitizer could presumably prevent the attacker from directly loading same-origin resources by rewriting URLs in untrusted markup, but if the attacker can make a request to their own server and then redirect it to the original application and make it appear as same-origin it opens up an interesting attack surface.

This also applies to navigation restrictions to mitigate XSS: if I configure my application to disallow cross-site requests to all endpoints other than /, but allow same-origin navigations to any endpoint, then any external link on my site would allow the target server to redirect the user back to an arbitrary same-origin URL, potentially with an XSS payload in the URL.

@anforowicz
Copy link
Author

Similar question came-up for PaymentRequest API for non-HTTP "redirects" (#30), but I also want to note that the initial PaymentRequest initiator-tracking implementation for HTTP-requests also doesn't look at the full URL chain - see here: https://chromium-review.googlesource.com/c/chromium/src/+/1636635/4/components/payments/core/payment_manifest_downloader.cc#187

@annevk
Copy link
Member

annevk commented Feb 18, 2020

In general you need to look at the full chain, A -> B -> A is not trustworthy.

@annevk
Copy link
Member

annevk commented Mar 10, 2021

I think this can be closed.

Perhaps an issue should be opened for payments so that they adopt a more secure model.

@mikewest
Copy link
Member

I think this can be closed.

Agreed.

Perhaps an issue should be opened for payments so that they adopt a more secure model.

Assuming https://w3c.github.io/payment-method-manifest/#fetch-the-web-app-manifest-for-a-default-payment-app is the feature in question, it should follow normal fetch semantics, so its fetch metadata behavior should be well-defined. If Chromium still diverges from that, we should consider it an implementation bug.

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