Anne van Kesteren

Same-origin policy

The same-origin policy, sometimes referred to as SOP, is the foundation of the web platform’s somewhat flawed security model. Without a browser, https://untrusted.example/ (Untrusted) can access any number of servers through curl. It cannot however access any servers located behind a firewall. With a browser, Untrusted can fetch resources from servers accessible to the user visiting Untrusted. Therefore, when a browser is involved the reach of Untrusted is what Untrusted can reach through curl plus with what the user can reach through curl. SOP prevents Untrusted from accessing the contents of resources on https://intranet.local/ (Intranet).

SOP also protects the contents of resources that depend on HTTP cookies and/or authentication (credentials). Most request contexts, such as img and script elements, include credentials in fetches by default. Thus if the user has stored credentials for https://credentialed.example/ (Credentialed), they will be included in outgoing fetches from Untrusted. (See ambient authority for why this might lead to problems.) Being able to access the contents of resources of Credentialed would be as problematic as accessing those of Intranet.

Because of SOP XMLHttpRequest has historically had a same-origin restriction. Reading the contents of resources of Credentialed and Intranet would be problematic. However, this also excludes access to notable non-Credentialed non-Intranet servers, such as https://example.com/ (Example). The problem with Example is that it cannot be distinguished from Intranet (private IPv4 address ranges are not reliably used). We invented CORS so that Untrusted can access the contents of resources on Example (and even on Credentialed and Intranet) as long as the resource opts in. To better understand CORS we first need to look at the historical request contexts.

At some point the same-origin policy did not exist and various requests could be made across origins, including credentials, leading to some leakage of Credentialed and Intranet data. Because of the network effects of the web these holes could not be fixed and are now enshrined and part of the security model:

Now as should be clear from above what CORS enables is allowing reading the contents of resources across origins (e.g. from Untrusted to Credentialed). To a far greater extent than the enshrined information leaks that already exist. CORS also enables the full power of XMLHttpRequest across origins. However, that would be far more than what has been traditionally allowed through images and forms (e.g. custom methods and headers). Therefore requests that use the full power require a CORS preflight request. The CORS preflight request confirms that the URL understands CORS. The final response still has to include the relevant CORS headers.

Hopefully this makes it clear that the same-origin policy solves a real problem. And that we need CORS due to Credentialed, Intranet, the more powerful requests it allows, and the inability to distinguish Example from either Credentialed or Intranet. This is also the reason we do not have a TCP API. It would be great to have a solution that would remove the need for CORS and allow a TCP API, but if you think you have one and it involves asking the user, think again. And if you want to expand the information leaks (e.g. allowing document.styleSheets without CORS), reconsider. All the information leaks we have are enshrined bugs from the time JavaScript became a thing.

(Please understand that this is introductory material. It is simplified somewhat for brevity.)