CORS Security

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