Same-Origin Policy And Cross-Origin Resource Sharing (CORS)
What Does "Origin" Mean?
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. For example, http://www.hackedu.io/about is the same origin as http://www.hackedu.io/contact in contrast to http://hackedu.io/about, which has a different origin (different hostname).
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 HTTP protocol was extremely simple when it was first created. At that time websites consisted of static, non-interactive pages. There were no animations or menus, and definitely no eye-catching designs or interactive pages.
Why The Same-Origin Policy Was Necessary?
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.
If the HTTP method is
HEAD and the Content-Type is
multipart/form-data, then it is a 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 (e.g., Access-Control-Allow-Origin: https://www.hackedu.io, http://example.com). However, it supports the wildcard operator (
*) which tells the server that any cross-request should be allowed. This is a bad idea unless you want anyone to consume your restful API.
Any request that is not considered Simple (i.e, uses a different HTTP method than
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
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 which 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.
CORS is just one method to relax same-origin policies. There are also other methods, such as JSON with Padding (JSONP) or cross-document messaging. However, the problem with JSONP is that it is read-only and became incapable of serving the requirements imposed by complex web applications. While CORS has its own downsides (e.g. you cannot specify multiple whitelisted domains), it is still a better choice than the other alternatives mentioned and it will most likely be improved in the future.