CORS: How to Use and Secure a CORS Policy with Origin

September 23, 2020
|
co-authored by Akash Shetty
|
8 min read

CORS (Cross-Origin Resource Sharing) enables resource sharing that pulls data from a lot of different sources. Like any relatively open aspect of the internet, it can be a risk. Learn how to test your web applications to create a secure CORS policy.

Origins and Key Concepts

CORS began as a way to make application resource sharing easier and more effective. With CORS, it is possible for one app to share resources with an application belonging to another domain. This leads to a web where many apps from various domains are sharing resources with one another.

For example, have you ever wondered how various airlines’ latest fares and other related data lands on your favorite travel booking app? That’s the power of the CORS policy. If you want to build an online travel booking application that integrates data and resources from all vendors and airlines, the easiest way would be to pull data from the vendor’s or airline’s APIs with CORS configured.

Specifically, CORS is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. A CORS request can be triggered by providing an additional header called “Origin” in the http request.

Figure 1: Travel Website integrates data from various other applications.

A misconfigured CORS policy comes with possible security vulnerabilities. Threat actors have been able to use it to obtain sensitive user data and steal bitcoin wallets.

Before getting into the specifics of CORS exploitation, we’ll discuss some core technical concepts.

A Brief History of Microservice Architecture

Before CORS was standardized, same origin policy (SOP) did not allow two web application domains with different origins to share resources. But today, the world is moving toward microservice architecture. This forms large, complex applications out of various small applications. For these small applications, resource sharing is very important. However, they are loosely coupled and deployed on different servers or hosted on different domains or ports. This is where standardized CORS plays a critical role. It allows resource sharing even when a http request is made to a different domain, different subdomain, different protocol or different port.

The Importance of SOP

SOP is a critical security mechanism that restricts how a document or script loaded from one origin can interact with a resource from another origin. It helps isolate potentially malicious documents, reducing possible attack vectors. Web applications are often hit or targeted with Cross Site Scripting (XSS) and Cross Site Request Forgery (CSRF) attacks. In response to this issue, web application developers have used SOP.

A key component of SOP is the idea of “origin”, which requires that two web pages in the same domain, host and protocol, are of the same origin. The format of origins traditionally follows ‘Origin= (domain, port, protocol)’.

For example, a domain running in a browser cannot access the data on other domains running in the same browser. This is because the web browser enforces the same origin policy, isolating the data being shared between two different applications on the same browser.

To better understand SOP, let’s use an example web URL: https://www.storecompany[.]com. The below table helps us understand more about ‘Same-Origin’.

 URL  Outcome
http://store.company[.]com/dir/page.html Starting URL
 http://store.company[.]com/dir2/other.html Same-Origin
 http://store.company[.]com/dir/inner/another.html Same-Origin
 https://store.company[.]com/page.html Different Protocol
 https://store.company[.]com:443/page.html Different Port
 http://swing.company[.]com/page.html Different Domain

How to Use CORS

A CORS request can be triggered by providing an additional header called “Origin” in the http request. For example, a client request with CORS origin header would look like this:

Get /data   http/1.1

Host: Service.domain-A.com

Origin: http://www.example[.]com

In this example, domain www.example[.]com wants to fetch resources from service.domain-A.com. If domain service.domain-A.com wishes to share its resources, then the requesting domain www.example[.]com should be mentioned in the “Access-Control-Allow-Origin” header server side. For example:

HTTP/1.1 200 OK

Access-Control-Allow-Origin: http://www.example[.]com

This response from server Service.domain-A.com shows that www.example[.]com can fetch its resources. Along with the “Access-Control-Allow-Origin” header, there are many more server side CORS configurations and policies that could be configured to enhance stricter policies.

The Keys to CORS Implementation: Headers

The CORS standard has been updated with multiple headers to control resource sharing policies across multiple domains. Examples of these headers are included below:

Request Header: Origin

The Origin request header indicates where a fetch originates from. It doesn’t include any path information, only the server name. It is sent from CORS requests, as well as with POST requests. It is similar to the Referrer header but, unlike that header, it doesn’t disclose the whole path.

Response Header: Access-Control-Allow-Origin

The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given origin.

Example of an Access-Control-Allow-Origin header:

Response Header: Access-Control-Allow Header

The Access-Control-Allow-Header’s response header is used in response to a preflight request which includes the Access-Control-Request-Header to indicate which HTTP headers can be used during the actual request. This header is required if the request has an Access-Control-Request Header.

Example of an Access-Control-Allow Header:

Response Header: Access-Control-Allow-Method

The Access-Control-Allow-Method’s response header specifies the method or methods allowed when accessing the resource in response to a preflight request.

Syntax:

Response Header: Access-Control-Allow-Credentials

The default behavior of CORS is for requests to be passed without credentials such as cookies or the authorization header. However, the cross-domain server can permit reading the response when credentials are passed to it by setting the CORS Access-Control-Allow-Credentials header to true. If the requesting website uses JavaScript to declare that it is sending cookies with the request, then the browser will permit the requesting website to read the response, because the Access-Control-Allow-Credentials response header is set to true.

Syntax:

Understanding CORS Pre-Flight Requests

Before you request a server or another domain’s resources, the browser/client sends a preflight HTTP request of the OPTIONS method to the server/domain. This appends three HTTP request headers and looks like Access-Control-Request-Method, Access-Control-Request-Headers, and Origin. This allows the receiving system to understand whether the requested ‘resource’ and action ‘method’ could be trusted and allowed by the server/domain. If the server does not wish to allow the requested method and resource, then the server rejects all the subsequent requests.

For example, a client might ask the server to perform a DELETE request. But, before sending the actual delete request, the browser makes an OPTIONS call with three http headers to find out which method the server supports.

If the server allows it, it will respond to the OPTIONS call with the ‘Access-Control-Allow-Method’ header which mentions the DELETE method.

OPTIONS /resource/bob

Access-Control-Request-Method: DELETE

Access-Control-Request-Headers: origin, x-requested-with

Origin: https://bob.alice[.]org

HTTP/1.1 204 No Content

Connection: keep-alive

Access-Control-Allow-Origin: https://bob.alice[.]org

Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE

Access-Control-Max-Age: 86400

Common Misconfigurations

Many developers have overlooked CORS. These common mistakes can be exploited by threat actors and/or reported on in bounty programs. In this section, we will discuss common misconfigurations of CORS that penetration testers and bounty experts might identify.

1. Setting Wildcard (*) in Response Header

The Allow-Access-Control-Origin response header is configured to allow and control the resources that need to be shared with another requesting domain. It is misconfigured or set to (*) by default. This means that any domain can access the server resources.

By crafting or writing an XHR (XML HTTP REQUEST), an attacker could redirect the resource to the attacker-controlled server, thus compromising the user’s sensitive and confidential information.

Verifying for (*) Wildcard Configuration

Request

GET /api/account-user

Host: www.mysite.com

Origin: www.mysite.com

In the below response, the header Allow-Access-Control-Origin is set to wildcard. It means any domain can access the resources.

Response

HTTP/1.0 200 OK

Access-Control-Allow-Origin: *

Access-Control-Allow-Credentials: true

2. Improper Regular Expression / Pre-Domain Wildcard (*) in Origin Header

In some cases, if the website or domain (example.com) wants to allow requests from its subdomains (ftp.example.com, dns.example.com etc.) the developers may use the below code to allow CORS.

In this code, any request containing (anystring).example[.]com is allowed to fetch resources from “example.com.” An attacker could create a fake website with the name “attacker.example.com” and force “example.com” to view it as a legitimate subdomain. This allows the legitimate site to share its resources with the attacker-controlled site.

In the second line of the code, “.” itself is a regular expression which means “any single character.” This would make the string “attackexample[.]com” match the regular expression. This leads to a situation where the machine would falsely interpret the string as a valid, allowed subdomain. In fact, it is a completely different domain. Therefore, an attacker could create a fake website “attackexample[.]com,” that would force the website “example.com” to share its sensitive resources.

Verifying for (*) Subdomain Wildcard Configuration

Request

GET /api/account user

Host: mysite.com

Origin: example.com

Response

HTTP/1.0 200 OK

Access-Control-Allow-Origin: *.example.com

Access-Control-Allow-Credentials: true

3. Null Origin Header

The specification of the Origin header supports the “null” value. If a cross-origin resource redirects to another resource at a new origin, the browser will set the value of the Origin header to “null” after redirecting. If the server supports the “null” Origin by allowing it in “Allow-Access-Control-Origin,” attackers could abuse this misconfiguration.

Verifying for Mis-Configured “Null” Origin

Request

GET /sensitive data
Host: vulnerablesite.com
Origin: null

Response

HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

In this situation, an attacker can use various tricks to generate a cross-domain request containing the value “null.” This will satisfy the whitelist, leading to cross-domain access. For example, this can be done using a sandboxed Inline Frame cross-Origin code.

Verifying Misconfiguration Using a Demo Application

The application and domain used below are part of a fictional internally hosted application. The domain corslab[.]com does not exist and is not a public domain, but it will be helpful as an illustration.

Step 1: Access the website using a proxy tool.

Step 2: Add “Origin” request header to verify the CORS configured by corslab[.]com

Step 3: The HTTP response below indicates that corslab[.]com allows its resources to be shared with any requesting Origin.

The above step shows how to verify a misconfigured CORS. This technique could also be leveraged by a threat actor using some JavaScript skills to exploit this vulnerability, so be aware of it if you’re diagnosing a problem.

CORS Mitigation and Best Practices

Organizations should test their mitigation measures for CORS to identify and remediate any security flaws. In particular, look for some common errors. Test whether cross-domain requests are allowed from any origin or not, thus opening them to content exfiltration attacks. Ensure the origin is properly specified in the ACAO if the web resource contains sensitive information. Additionally, confirm that arbitrary input of the origin should not be allowed, as dynamically reflected origin is readily exploitable. Lastly, avoid whitelisting null and wildcards in internal networks. Cross-domain requests and sandboxed requests can specify the null origin. And, when you can, use a skilled group of penetration testers to assess your organization’s adherence to CORS best practices.

Tags: 
 | 
Ramandeep Kaur
Software Engineer, PTC IBM

Ramandeep works as a Software Engineer for IBM PTC at India Software Labs. She has 6 years of experience in varied roles in IT and worked for projects under ...
read more