How does setting Origin to null in a redirected CORS request protect against a confused deputy attack?
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.
Look at the Origin header in terms of CSRF defense.
Let's say a.com hosts...
- a service that uses the Origin header to block CSRF attacks,
- and a web page that makes CORS requests to b.com.
If the Origin was retained after cross-origin redirects, the following CSRF attack would be possible:
- A user signs in to the a.com website.
- The user visits a page that makes a CORS request to b.com with
Origin: https://a.com
. - b.com responds with a redirect to a CSRF-protected endpoint on a.com.
- The user's browser follows the redirect and requests the URL with
Origin: https://a.com
. 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.