#
CORS (Cross-Origin Resource Sharing) Misconfiguration
A security vulnerability where improper CORS configuration allows unauthorized websites to access sensitive data from your API or web application.
CRITICAL VULNERABILITY API SECURITY OWASP TOP 10
#
What is CORS Misconfiguration?
Danger Alert
CORS misconfigurations can expose your users' sensitive data to malicious websites, leading to account takeovers, data theft, and privacy breaches. This is one of the most common API security issues.
In Simple Terms:
Imagine you have a bank vault (your API) that should only allow authorized bank employees (your website) to access customer records. CORS is like the security guard who checks IDs.
A CORS misconfiguration is like the security guard saying: "Anyone can enter! I don't care who you are!"
Now a thief (malicious website) can walk in, pretend to be a bank employee, and steal customer information while the real customer is logged into their online banking.
#
Real-World Analogy
Think of CORS like a shared office building's access control system:
Your API server is like an office building with valuable data inside.
Your legitimate website (example.com) is like an authorized tenant with a valid keycard.
CORS is the security guard who checks if visitors are allowed to access specific offices.
Misconfigured CORS is like the security guard who:
- Lets ANYONE into ANY office (Access-Control-Allow-Origin: *)
- Accepts fake keycards (Trusts any origin that claims to be authorized)
- Doesn't verify credentials (Allows credentials with wildcard origins)
#
How CORS Misconfiguration Works
#
The Attack Process
Victim browses to evil-site.com while logged into bank.com in another tab.
Evil site makes JavaScript request to bank.com/api/account-details using victim's cookies.
Browser sends preflight request asking: "Can evil-site.com access bank.com API?"
Bank's API responds: Access-Control-Allow-Origin: * or reflects the origin back.
Browser allows the request, evil site receives victim's account data and sends it to attacker.
#
Types of CORS Misconfigurations
#
1. Wildcard Origin with Credentials
CRITICAL
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Most Dangerous Configuration
This combination is supposed to be blocked by browsers, but older browsers or poorly configured proxies might allow it, exposing all authenticated user data.
#
2. Reflected Origin Vulnerability
HIGH RISK
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/user-data')
def get_user_data():
origin = request.headers.get('Origin')
# VULNERABLE: Reflects any origin back
response = jsonify({'email': 'user@example.com', 'balance': 50000})
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
app.get('/api/user-data', (req, res) => {
const origin = req.headers.origin;
// VULNERABLE: Reflects any origin
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.json({ email: 'user@example.com', balance: 50000 });
});
<?php
$origin = $_SERVER['HTTP_ORIGIN'];
// VULNERABLE: Accepts any origin
header("Access-Control-Allow-Origin: $origin");
header("Access-Control-Allow-Credentials: true");
echo json_encode([
'email' => 'user@example.com',
'balance' => 50000
]);
?>
#
3. Weak Origin Validation
MEDIUM RISK
# VULNERABLE: Weak regex validation
origin = request.headers.get('Origin')
# This accepts: evil-example.com, example.com.evil.com, etc.
if 'example.com' in origin:
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
#
4. Null Origin Acceptance
MEDIUM RISK
// VULNERABLE: Accepts null origin
const origin = req.headers.origin;
if (origin === 'null') {
res.setHeader('Access-Control-Allow-Origin', 'null');
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
Null Origin Attack
Attackers can send requests with Origin: null using sandboxed iframes or data URIs, bypassing origin checks.
#
5. Pre-Domain Wildcard
MEDIUM RISK
Access-Control-Allow-Origin: *.example.com
Browser Limitation
Browsers don't support subdomain wildcards in CORS headers. However, some CDNs or proxies might implement this incorrectly.
#
Real-World Examples
#
Case Study 1: Bitcoin Exchange API Leak (2018)
A major cryptocurrency exchange had CORS misconfiguration on their trading API.
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Attackers created phishing sites that made requests to the exchange's API, stealing:
- User balances
- Trading history
- API keys
- Wallet addresses
- $2.3M in cryptocurrency stolen
- 10,000+ user accounts compromised
- Exchange forced to halt trading for 72 hours
- $15M in compensation paid to affected users
#
Case Study 2: Social Media Platform Data Breach (2019)
150M Users Affected
Major Privacy Breach
Reflected origin vulnerability allowed malicious websites to access private user messages, photos, and contact lists.
Technical Details:
# Their vulnerable code
@app.route('/api/messages')
def get_messages():
origin = request.headers.get('Origin')
response = jsonify(get_user_messages())
response.headers['Access-Control-Allow-Origin'] = origin # Reflected!
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
Consequences:
- 150 million user accounts affected
- Private messages and photos exposed
- $5 billion FTC fine
- Congressional hearings on data privacy
#
Case Study 3: Healthcare Portal CORS Issue (2020)
Electronic health records (EHR) system with vulnerable API
// Vulnerable whitelist check
const allowedOrigins = [
'hospital.com',
'patient-portal.com'
];
const origin = req.headers.origin;
// VULNERABLE: Substring match instead of exact match
if (allowedOrigins.some(allowed => origin.includes(allowed))) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
Attacker registered: hospital.com.evil-phishing.com
- 45,000 patient records exposed
- HIPAA violations: $4.5M fine
- Class action lawsuit: $12M settlement
#
How to Detect CORS Misconfigurations
#
Manual Testing
Use browser DevTools or Burp Suite to capture an API request
Send request with malicious origin:
GET /api/sensitive-data HTTP/1.1
Host: target.com
Origin: https://evil.com
Cookie: session=abc123
Look for:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
If present, you can exfiltrate data from JavaScript on evil.com
#
Testing Script
import requests
def test_cors_misconfiguration(target_url):
"""Test for CORS misconfigurations"""
test_origins = [
'https://evil.com',
'null',
'https://target.com.evil.com',
'https://evil-target.com'
]
vulnerabilities = []
for origin in test_origins:
headers = {
'Origin': origin,
'Cookie': 'session=test123' # Use your session cookie
}
response = requests.get(target_url, headers=headers)
allow_origin = response.headers.get('Access-Control-Allow-Origin')
allow_creds = response.headers.get('Access-Control-Allow-Credentials')
if allow_origin and allow_creds == 'true':
if allow_origin == origin or allow_origin == '*':
vulnerabilities.append({
'origin': origin,
'allowed': allow_origin,
'credentials': allow_creds
})
print(f"[!] VULNERABLE: {origin} is allowed with credentials!")
return vulnerabilities
# Test your API
vulns = test_cors_misconfiguration('https://api.example.com/user-data')
if vulns:
print(f"\n[!] Found {len(vulns)} CORS misconfigurations!")
else:
print("[+] No CORS vulnerabilities detected")
const axios = require('axios');
async function testCORSMisconfiguration(targetUrl) {
const testOrigins = [
'https://evil.com',
'null',
'https://target.com.evil.com',
'https://evil-target.com'
];
const vulnerabilities = [];
for (const origin of testOrigins) {
try {
const response = await axios.get(targetUrl, {
headers: {
'Origin': origin,
'Cookie': 'session=test123'
}
});
const allowOrigin = response.headers['access-control-allow-origin'];
const allowCreds = response.headers['access-control-allow-credentials'];
if (allowOrigin && allowCreds === 'true') {
if (allowOrigin === origin || allowOrigin === '*') {
vulnerabilities.push({
origin,
allowed: allowOrigin,
credentials: allowCreds
});
console.log(`[!] VULNERABLE: ${origin} is allowed with credentials!`);
}
}
} catch (error) {
console.log(`[-] Error testing ${origin}:`, error.message);
}
}
return vulnerabilities;
}
// Test your API
testCORSMisconfiguration('https://api.example.com/user-data')
.then(vulns => {
if (vulns.length > 0) {
console.log(`\n[!] Found ${vulns.length} CORS misconfigurations!`);
} else {
console.log('[+] No CORS vulnerabilities detected');
}
});
#
Automated Tools
Security Testing Tools
- Burp Suite CORS Scanner - Professional web security testing
- OWASP ZAP - Free automated CORS testing
- CORStest - Dedicated CORS misconfiguration scanner
- cors-scanner - CLI tool for batch testing
#
Prevention Strategies
#
1. Strict Whitelist Validation
RECOMMENDED
from flask import Flask, request, jsonify
from urllib.parse import urlparse
app = Flask(__name__)
# Strict whitelist of allowed origins
ALLOWED_ORIGINS = [
'https://www.example.com',
'https://app.example.com',
'https://mobile.example.com'
]
def validate_origin(origin):
"""Validate origin with exact match"""
if not origin:
return False
# Parse and validate
try:
parsed = urlparse(origin)
# Exact match only (no substrings!)
if origin in ALLOWED_ORIGINS:
return True
return False
except Exception:
return False
@app.route('/api/user-data')
def get_user_data():
origin = request.headers.get('Origin')
response = jsonify({
'email': 'user@example.com',
'balance': 50000
})
# Only set CORS headers if origin is whitelisted
if validate_origin(origin):
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Vary'] = 'Origin' # Important for caching!
return response
@app.route('/api/user-data', methods=['OPTIONS'])
def preflight():
"""Handle CORS preflight"""
origin = request.headers.get('Origin')
if not validate_origin(origin):
return '', 403 # Forbidden
response = app.make_response('')
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Max-Age'] = '86400' # 24 hours
response.headers['Vary'] = 'Origin'
return response
const express = require('express');
const cors = require('cors');
const app = express();
// Strict whitelist
const ALLOWED_ORIGINS = [
'https://www.example.com',
'https://app.example.com',
'https://mobile.example.com'
];
// CORS configuration
const corsOptions = {
origin: function (origin, callback) {
// Allow requests with no origin (mobile apps, Postman, etc.)
if (!origin) {
return callback(null, true);
}
// Exact match validation
if (ALLOWED_ORIGINS.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true, // Allow credentials
optionsSuccessStatus: 200,
maxAge: 86400 // 24 hours
};
// Apply CORS middleware
app.use(cors(corsOptions));
app.get('/api/user-data', (req, res) => {
res.json({
email: 'user@example.com',
balance: 50000
});
});
app.listen(3000);
<?php
// Strict whitelist
$allowedOrigins = [
'https://www.example.com',
'https://app.example.com',
'https://mobile.example.com'
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
// Validate origin with exact match
if (in_array($origin, $allowedOrigins, true)) {
header("Access-Control-Allow-Origin: $origin");
header("Access-Control-Allow-Credentials: true");
header("Vary: Origin");
// Handle preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Max-Age: 86400");
http_response_code(200);
exit;
}
} else {
http_response_code(403);
exit('Origin not allowed');
}
// Return data
echo json_encode([
'email' => 'user@example.com',
'balance' => 50000
]);
?>
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class CORSFilter implements Filter {
private static final List<String> ALLOWED_ORIGINS = Arrays.asList(
"https://www.example.com",
"https://app.example.com",
"https://mobile.example.com"
);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String origin = req.getHeader("Origin");
// Validate origin with exact match
if (origin != null && ALLOWED_ORIGINS.contains(origin)) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Vary", "Origin");
// Handle preflight
if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
res.setHeader("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE");
res.setHeader("Access-Control-Allow-Headers",
"Content-Type, Authorization");
res.setHeader("Access-Control-Max-Age", "86400");
res.setStatus(HttpServletResponse.SC_OK);
return;
}
} else if (origin != null) {
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
chain.doFilter(request, response);
}
}
#
2. Environment-Based Configuration
import os
class CORSConfig:
"""Environment-based CORS configuration"""
PRODUCTION_ORIGINS = [
'https://www.example.com',
'https://app.example.com'
]
STAGING_ORIGINS = [
'https://staging.example.com',
'https://staging-app.example.com'
]
DEVELOPMENT_ORIGINS = [
'http://localhost:3000',
'http://localhost:8080',
'http://127.0.0.1:3000'
]
@staticmethod
def get_allowed_origins():
env = os.getenv('ENVIRONMENT', 'development')
if env == 'production':
return CORSConfig.PRODUCTION_ORIGINS
elif env == 'staging':
return CORSConfig.STAGING_ORIGINS
else:
return CORSConfig.DEVELOPMENT_ORIGINS
ENVIRONMENT=production
ALLOWED_ORIGINS=https://www.example.com,https://app.example.com
ENVIRONMENT=development
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
#
3. Security Headers Best Practices
def add_security_headers(response):
"""Add comprehensive security headers"""
# CORS headers (already set based on whitelist)
# response.headers['Access-Control-Allow-Origin'] = validated_origin
# response.headers['Access-Control-Allow-Credentials'] = 'true'
# Important: Vary header for caching
response.headers['Vary'] = 'Origin'
# Additional security headers
response.headers['X-Content-Type-Options'] = 'nosniff'
response.headers['X-Frame-Options'] = 'DENY'
response.headers['X-XSS-Protection'] = '1; mode=block'
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
# Content Security Policy
response.headers['Content-Security-Policy'] = "default-src 'self'"
return response
#
4. Avoid Credentials with Public APIs
Public API Best Practice
For public APIs that don't need authentication, avoid using credentials entirely:
// Public API - no credentials needed
app.use(cors({
origin: '*', // Allow all origins
credentials: false // NO credentials - this is safe for public data
}));
app.get('/api/public-data', (req, res) => {
// No authentication required
res.json({ publicInfo: 'This is public data' });
});
#
Security Checklist
#
Development Phase
- Define strict whitelist of allowed origins
- Use exact match validation (no substring matching)
- Never reflect
Originheader without validation - Never use
Access-Control-Allow-Origin: *with credentials - Implement proper preflight (OPTIONS) request handling
- Add
Vary: Originheader for proper caching - Use environment-based configuration
- Implement comprehensive error logging
#
Testing Phase
- Test with malicious origins (evil.com, null, etc.)
- Test with subdomain variations (target.com.evil.com)
- Verify wildcard (*) is not used with credentials
- Test preflight request handling
- Verify exact match validation (not substring)
- Test with different HTTP methods (GET, POST, PUT, DELETE)
- Test credential handling with cross-origin requests
- Use automated CORS scanners (Burp Suite, ZAP)
#
Production Deployment
- Review and minimize allowed origins list
- Enable CORS logging and monitoring
- Set up alerts for CORS violations
- Implement rate limiting on API endpoints
- Use Web Application Firewall (WAF) with CORS rules
- Regular security audits and penetration testing
- Keep CORS middleware/libraries updated
- Document CORS policy and exceptions
#
Testing Tools & Resources
#
Professional Tools
CORS Scanner Extension
- Automated CORS misconfiguration detection
- Preflight request analysis
- Credential-based testing
- Download Burp Suite
Free Open-Source Scanner
- Active/passive CORS scanning
- API endpoint testing
- Report generation
- Download ZAP
Dedicated CORS Testing
npm install -g corstest
corstest https://api.example.com/endpoint
#
Online Resources
#
Key Takeaways
Remember These Critical Points
Never use wildcard with credentials:
Access-Control-Allow-Origin: *+Access-Control-Allow-Credentials: trueis dangerousUse exact match validation: Don't check if origin contains your domain - use exact match from whitelist
Don't reflect origins blindly: Never echo back the
Originheader without strict validationReject null origin:
Origin: nullcan be exploited via sandboxed iframesAdd Vary header: Always include
Vary: Originfor proper caching behaviorMinimize origin whitelist: Only add origins that absolutely need access
Monitor CORS violations: Log and alert on rejected CORS requests
Test thoroughly: Use both manual and automated testing tools
#
How Layerd AI Protects Against CORS Attacks
Layerd AI's advanced security platform provides comprehensive protection against CORS misconfigurations:
- Automatic CORS Analysis: Continuously monitors and validates CORS configurations across all API endpoints
- Origin Validation: Enforces strict whitelist validation with exact matching
- Credential Protection: Detects and prevents dangerous credential + wildcard combinations
- Real-time Monitoring: Alerts on suspicious cross-origin requests and potential data exfiltration
- Configuration Hardening: Automatically suggests secure CORS configurations based on your application needs
- Compliance Enforcement: Ensures CORS policies meet security standards and regulatory requirements
Protect your APIs from CORS attacks with Layerd AI's intelligent security platform.
#
Additional Resources
- CORS Specification (W3C)
- Mozilla CORS Guide
- OWASP API Security Top 10
- Exploiting CORS Misconfigurations (PortSwigger)
- CORS Testing Cheat Sheet
Last Updated: November 2025