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’.
Scroll to view full table
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
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
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.
Response Header: Access-Control-Allow-Credentials
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.
Access-Control-Request-Headers: origin, x-requested-with
HTTP/1.1 204 No Content
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
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
In the below response, the header Allow-Access-Control-Origin is set to wildcard. It means any domain can access the resources.
HTTP/1.0 200 OK
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
GET /api/account user
HTTP/1.0 200 OK
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
GET /sensitive data
HTTP/1.1 200 OK
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.
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.