11

Excerpt from Here:

If a cross-origin resource redirects to another resource at a new origin, the browser will set the value of the Origin header to null after redirecting. This prevents additional confused deputy attacks, but a cost of making it difficult to transparently move CORS resources that support (cookie-based) credentials and simple requests across domains with 3xx status codes as one can with non-CORS resources. It is possible to redirect same-origin resources to a cross-origin location (single hop only) because browsers will transparently apply the CORS algorithm to such requests and include the Origin header for the first hop.

How would keeping the origin header for the redirected request allow for a confused deputy attack?

Raniz
  • 233
  • 2
  • 10
  • the origin header must match the domain, null doesn't match anything. – dandavis Oct 21 '17 at 08:07
  • It's set to the string *null* which you can match if you want to (but you shouldn't), but that still doesn't explain how you can execute a confused deputy attack if the origin header isn't changed. – Raniz Oct 21 '17 at 12:43
  • once it sets the origin to null, no more bounces because the following origin check fails. – dandavis Oct 21 '17 at 21:08
  • 1
    Yes, obviously, but how does that protect against a confused deputy attack? What attack would be possible if the following origin check passes? – Raniz Oct 22 '17 at 15:17
  • unless the request kept a log of each bounce, the 3rd hop would have no knowledge of the originating server with which to validate the request against a cors domain spec. if it's approved by default, bingo. – dandavis Oct 23 '17 at 07:55

2 Answers2

8

That paragraph is tersely written, and the use of "redirects to another resource at a new origin" in the first sentence isn't quite right.

Here's a simple contrived example. Let's say you are malicious, and there is a web application that uses the services of a privileged API via CORS, so the web application's Origin is trusted by the privileged API. And let's say you want to get access to the data behind that privileged API, but your Origin of course is not trusted.

You create a simple useful service that you offer via CORS, and you get the web application to include your service in a page- any page- under its trusted Origin. That page does not need to access the privileged API.

(Of course, once you're in the page of your victim you can do whatever you want, but bear with me.)

If you decide to change your CORS service from issuing a 200 with some data to issue a 3xx to the privileged API- crossing resource domains- this creates a trust problem.

The actual Origin- the page that embedded your resource- will be trusted by the privileged API. But it didn't issue the request, and it may not want to be talking to the privileged API at this particular point in time.

Instead, you issued the redirect, and while you are trusted, in part, by the Origin, you are not trusted by the privileged API. If the browser follows your 3xx and sends along the Origin, you get to illegitimately piggy back on the trust given by the privileged API to the Origin.

What is the browser to do? A reasonable answer is to not follow the 3xx at all, but that would disallow use cases for which trust is not concern. Issuing the request with a "null" Origin allows those use cases, but prevents the exploitation of trust that sending along the original Origin header would allow.

Jonah Benton
  • 3,359
  • 12
  • 20
  • Thanks for the example, contrived or not :) IMO a better solution would be to include a redirect stack or something in the CORS preflight check. – Raniz Apr 26 '18 at 23:31
  • 1
    Yeah, maybe; it is hard really convey this kind of chain, though, especially when there may be different trust domains involved for the same Origin. CORS is only domain specific, not URI specific. – Jonah Benton Apr 27 '18 at 02:37
5

Look at the Origin header in terms of CSRF defense.

Let's say a.com hosts...

If the Origin was retained after cross-origin redirects, the following CSRF attack would be possible:

  1. A user signs in to the a.com website.
  2. The user visits a page that makes a CORS request to b.com with Origin: https://a.com.
  3. b.com responds with a redirect to a CSRF-protected endpoint on a.com.
  4. The user's browser follows the redirect and requests the URL with Origin: https://a.com.
  5. The a.com server processes the request because it believes it originated from a.com, which is not completely accurate because...

    • b.com had full control over the requested URL, including the query parameters.
    • any data in the request body would have been originally intended for b.com, not a.com, and the data payload may have been carefully crafted to be harmful when submitted to the a.com endpoint. (Let's say it was a POST with a 307 redirect.)

The fact that a.com makes CORS requests to b.com does not imply that it trusts b.com completely.

This is discussed in a related Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=465517


If the Origin header contained a list of all the origins in the redirect chain, it might be possible to build applications that tolerated cross-origin redirects securely. Unfortunately the browser vendors never implemented that. From the same CORS for Developers document quoted in the question:

Although the CORS specification implies that you can list multiple origins in the Access-Control-Allow-Origin header, in practice only a single value is allowed by all modern browsers. The multiple value syntax was intended to allow all origins in a redirect chain to be listed, as allowed by RFC6454, but this was never implemented.

Andre D
  • 161
  • 1
  • 6