CORS

Same-Origin Policy

The same-origin policy is an important security feature of any modern browser. Its purpose is to restrict cross-origin interactions between documents, scripts, or media files from one origin to a web page with a different origin.

The term of origin in this context denotes the exact location of a specific resource (image, script, etc.). It consists of three main elements: the protocol (e.g., HTTP or HTTPS), the hostname (e.g., hackedu.io) and the port (80, 443, 8080, etc.). When the browser performs same-origin policy (SOP) checks, it compares the originating location with the location of the requested resource. If they perfectly match, then the origin is the same and the browser allows the requested resource to be loaded.

Why The Same-Origin Policy Was Necessary?

Let's suppose someone manages to trick you into visiting https://faceboook.com (extra "o"). On this website, there is an iframe which loads the content of the correct site https://facebook.com. As usual, you login into your Facebook account. Now, the malicious website (faceboook.com) can read your private messages or perform actions on your behalf with just a few Asynchronous JavaScript and XML (AJAX) requests. (CSRF).

What Does The Same-Origin Policy Allow?

It is a common misconception that same-origin policy blocks all cross-origin resources. If that were true Content Delivery Networks (CDNs) wouldn't exist. There are several HTML tags that generally allow embedded cross-origin resources: iframe, img, script, video, link, object, embed, form. Please note that they do not also permit cross-origin read. The difference between embedding and reading a resource is that when embedded, the resource is copied from the external origin and rendered locally, while reading the resource means their origin is preserved.

Although same-origin policy is an important browser security feature that provides significant protection against malicious scripts, it is far from perfect. In some cases it is not restrictive enough and common web vulnerabilities such as Cross-Site Request Forgery (CSRF) arise.

In other cases the policy is too restrictive and tangles the workflow of the web application. Engineers introduced a standard called Cross-Origin Resource Sharing as a way to relax the same-origin policy's restrictions.

What Is CORS?

Cross-Origin Resource Sharing (CORS) allows servers to specify trusted origins that can be used in cross-origin requests.

A CORS request can be either Simple or Preflight.

Simple request criteria:

  • HTTP method is GET, POST, or HEAD

  • and the Content-Type is text/plain, application/x-www-form-urlencoded or multipart/form-data

Simple request and can be initiated without any preliminary checks. In this case, the browser adds an Origin: header describing the origin from where the request has been initiated. Once the request is received the server tells the browser if the CORS request is valid by appending the Access-Control-Allow-Origin header to the response.

Here is an example to make things clear:

Let's suppose that you navigate to https://www.hackedu.io and your browser makes a request in the background to an API to get all available courses. It should look like this:

GET /courses HTTP/1.1 Host: api.hackedu.io Origin: https://www.hackedu.io ...

And the server responds with:

HTTP/1.1 200 OK Access-Control-Allow-Origin: https://www.hackedu.io Content-Type: application/json ...

Once the browser receives the HTTP response, it checks whether the request origin matches the value of Access-Control-Allow-Origin header. If the check fails, the response is blocked immediately. Also, one important note is that the Access-Control-Allow-Origin header supports only a single origin. This means you cannot specify multiple websites as the value of this header.

Any request that is not considered Simple (i.e, uses a different HTTP method than GET, POST, or HEAD, or the Content-Type is not one of those mentioned above) is called a Preflight request. This is because the browser sends a preflight request before the original request to make sure that the original request is acceptable to the server.

Below is an example:

Let's suppose your browser wants to send a DELETE request to https://api.hackedu.io/account/me. Since the HTTP method is not GET, POST, or HEAD, the browser initiates a preflight request that looks like this:

OPTIONS /account/me HTTP/1.1 Origin: https://hackedu.io Access-Control-Request-Method: DELETE

Once the server receives the preflight request it responds with headers that tell if the preflight request has been accepted:

HTTP/1.1 200 OK Access-Control-Allow-Origin: https://hackedu.io Access-Control-Allow-Methods: GET,DELETE,HEAD,OPTIONS

Now, the original DELETE request can be forwarded to the server. Also, it will contain the Origin header similar to the Simple requests.

Headers

The HTTP response headers:

  • Access-Control-Allow-Origin: | *

  • Access-Control-Expose-Headers

  • Access-Control-Max-Age: - indicates how long the results of a preflight request can be cached. For an example of a preflight request

  • Access-Control-Allow-Credentials: true - indicates whether or not the response to the request can be exposed when the credentials flag is true

  • Access-Control-Allow-Methods: [, ]*

  • Access-Control-Allow-Headers - indicate which HTTP headers can be used when making the actual request

The HTTP request headers:

  • Origin: - indicates the origin of the cross-site access request or preflight request

  • Access-Control-Request-Method: - used when issuing a preflight request to let the server know what HTTP method will be used when the actual request is made.

  • Access-Control-Request-Headers - used when issuing a preflight request to let the server know what HTTP headers will be used when the actual request is made

Last updated

Was this helpful?