Same Origin Policy and CORS

What is Origin

The protocol/domain/port tuple of a URL is called the origin

let url = 'http://store.company.com/dir/page.html'
//http://store.company.com is origin

When the two Origins are the same

When the protocol/domain/port tuple of the URL is the same for two URLs than those two urls are said to have the same origin.

What is the same origin Policy?

The same-origin policy is a critical security mechanism that restricts how a document or script loaded by one origin can interact with a resource from another origin. The script loaded by evil.com can’t interact with resources from a different origin.

Communication between cross-origin and their categories

cross-communication between cross-origin is divided into three categories

  1. Cross-Origin Writes: All cross-origin redirects, clicking on link, http requests calling, comes under this category. It is allowed by default.

  2. Cross-Origin Embeds: sub resources loading by <img>, <link>, <script>, <video>, <audio>, <embed>, <iframe> comes under this category. It is allowed by default.

  3. Cross-Origin Reads: Reading of subresources or responses loaded by ajax/fetch calls comes under this category. It is restricted by default.

Role of CORS in cross-origin communication

With CORS (cross-origin resource sharing) we ensure safer cross-origin reads.

How CORS works under the hood for HTTP requests

There are two types of cross-origin requests:

  1. Safe requests.

  2. All the others (Unsafe Request, Request with Credentials).

A request is safe if it satisfies two conditions:

  1. Safe method: GET, POST, or HEAD

  2. Safe headers – the only allowed custom headers are:

    • Accept,

    • Accept-Language,

    • Content-Language,

    • Content-Type with the value application/x-www-form-urlencoded, multipart/form-data or text/plain.

Any other request is considered “unsafe”. For instance, a request with PUT method or with an API-Key HTTP-header does not fit the limitations.

Safe Requests

GET /request
Host: anywhere.com
Origin: <https://javascript.info>
...

The server can inspect the Origin and, if it agrees to accept such a request, add a special header Access-Control-Allow-Origin to the response. That header should contain the allowed origin (in our case https://javascript.info), or a star *. Then the response is successful, otherwise, it’s an error.

The browser plays the role of a trusted mediator here:

  1. It ensures that the correct Origin is sent with a cross-origin request.

  2. It checks for permitting Access-Control-Allow-Origin in the response, if it exists, then JavaScript is allowed to access the response, otherwise, it fails with an error.

200 OK
Content-Type:text/html; charset=UTF-8
Content-Length: 12345
Content-Encoding: gzip
API-Key: 2c9de507f2c54aa1
Access-Control-Allow-Origin: <https://javascript.info>
Access-Control-Expose-Headers: Content-Encoding,API-Key

Unsafe Request

We can use any HTTP-method: not just GET/POST, but also PATCH, DELETE and others.

Some time ago no one could even imagine that a webpage could make such requests. So there may still exist webservices that treat a non-standard method as a signal: “That’s not a browser”. They can take it into account when checking access rights.

So, to avoid misunderstandings, any “unsafe” request – that couldn’t be done in the old times, the browser does not make such requests right away. First, it sends a preliminary, so-called “preflight” request, to ask for permission.

A preflight request uses the method OPTIONS, no body and three headers:

  • Access-Control-Request-Method the header has the method of the unsafe request.

  • Access-Control-Request-Headers the header provides a comma-separated list of its unsafe HTTP headers.

  • Origin the header tells from where the request came. (such as https://javascript.info)

If the server agrees to serve the requests, then it should respond with an empty body, status 200, and headers:

  • Access-Control-Allow-Origin must be either `` or the requesting origin, such as https://javascript.info, to allow it.

  • Access-Control-Allow-Methods must have the allowed method.

  • Access-Control-Allow-Headers must have a list of allowed headers.

  • Additionally, the header Access-Control-Max-Age may specify a number of seconds to cache the permissions. So the browser won’t have to send a preflight for subsequent requests that satisfy given permissions.

After a successful preflight request, the main HTTP request is made as shown in the safe request.

Request with Credentials

The most interesting capability exposed by both XMLHttpRequest Fetch and CORS is the ability to make "credentialed" requests that are aware of HTTP cookies and HTTP Authentication information. By default, in cross-origin XMLHttpRequest or Fetch invocations, browsers will not send credentials. A specific flag has to be set on the XMLHttpRequest object or the Request constructor when it is invoked.

Why?

That’s because a request with credentials is much more powerful than one without them. If allowed, it grants JavaScript the full power to act on behalf of the user and access sensitive information using their credentials.

To send credentials in fetch, we need to add the option credentials: "include" and in XMLHttpRequest we need to add withcredentials: true. If the server agrees to accept the request with credentials, it should add a header Access-Control-Allow-Credentials: trueto the response, in addition to Access-Control-Allow-Origin.

1
Subscribe to my newsletter

Read articles from Shailendra Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shailendra Singh
Shailendra Singh