CORS Misconfiguration Vulnerabilities โ The Three That Actually Get Exploited
Updated April 2026
Reading this article? Verify your fix in real-time. Test your CORS config for vulnerabilities โ CORSFixer
A CORS error means your config is too strict. A CORS misconfiguration means it is too loose โ and attackers can exploit it to steal data. These three patterns account for most real-world CORS vulnerabilities.
Vulnerability 1 โ Origin Reflection
The server reflects whatever origin the request sends back in the response. An attacker from evil.com sends a request โ the server responds with Access-Control-Allow-Origin: https://evil.com.
# VULNERABLE โ reflects any origin
app.use((req, res, next) => { res.setHeader("Access-Control-Allow-Origin", req.headers.origin); // โ res.setHeader("Access-Control-Allow-Credentials", "true"); next();
});
# SAFE โ allowlist check
const ALLOWED = new Set(["https://yourapp.com", "https://staging.yourapp.com"]);
app.use((req, res, next) => { const origin = req.headers.origin; if (ALLOWED.has(origin)) { res.setHeader("Access-Control-Allow-Origin", origin); res.setHeader("Vary", "Origin"); } next();
});
Vulnerability 2 โ Null Origin
Browsers send Origin: null for requests from sandboxed iframes, file:// URLs, and some redirects. Allowing null origin lets attackers use a sandboxed iframe to make credentialed requests.
# VULNERABLE
if (origin === "null") { res.setHeader("Access-Control-Allow-Origin", "null"); // โ
}
# SAFE โ never allowlist null
const ALLOWED = new Set(["https://yourapp.com"]);
if (ALLOWED.has(origin)) { // null is never in ALLOWED res.setHeader("Access-Control-Allow-Origin", origin);
}
Vulnerability 3 โ Regex Bypass
Substring and suffix checks are bypassed by domains that match the pattern but are controlled by an attacker.
# VULNERABLE โ substring check
if (origin.includes("yourapp.com")) { // โ evil-yourapp.com matches
# VULNERABLE โ endsWith check
if (origin.endsWith("yourapp.com")) { // โ yourapp.com.evil.com matches
# SAFE โ exact set membership
const ALLOWED = new Set([ "https://yourapp.com", "https://www.yourapp.com", "https://staging.yourapp.com",
]);
if (ALLOWED.has(origin)) { ... }
Test your API for these vulnerabilities
# Test origin reflection curl -s -I -H "Origin: https://evil.com" \ https://api.yourapp.com/endpoint | grep -i access-control-allow-origin # If it returns: Access-Control-Allow-Origin: https://evil.com โ VULNERABLE # Test null origin curl -s -I -H "Origin: null" \ https://api.yourapp.com/endpoint | grep -i access-control-allow-origin # If it returns: Access-Control-Allow-Origin: null โ VULNERABLE # Test regex bypass curl -s -I -H "Origin: https://evil-yourapp.com" \ https://api.yourapp.com/endpoint | grep -i access-control-allow-origin # If it returns that origin โ VULNERABLE
The safe pattern for every stack
// The one correct CORS origin check
const ALLOWED_ORIGINS = new Set([ "https://yourapp.com", "https://staging.yourapp.com", // Never add: *, null, or anything dynamic
]);
function getCorsOrigin(requestOrigin) { return ALLOWED_ORIGINS.has(requestOrigin) ? requestOrigin : null;
} Test your CORS config for vulnerabilities โ CORSFixer