How reflecting untrusted Origin headers and combining Access-Control-Allow-Origin with Allow-Credentials exposes authenticated APIs to cross-site data theft.
The Same-Origin Policy stops evil.com from reading your emails on gmail.com. CORS is an exception mechanism: gmail.com can say 'I officially give evil.com permission to read my user's data'. A CORS misconfiguration is when a lazy developer writes code that says 'Whoever is asking, copy their name and give them permission to read everything, including login cookies'.
A logged-in user visits attacker-site.com.
The malicious site executes: fetch('https://api.target.com/user/private-keys', {credentials: 'include'}).
The server receives Origin: https://attacker-site.com and responds with Access-Control-Allow-Origin: https://attacker-site.com and Access-Control-Allow-Credentials: true.
The browser allows attacker-site.com to read the victim's private API response and transmit it to the attacker's collection server.
// VULNERABLE: Dynamic Origin Reflection with Credentials
const express = require("express");
const app = express();
app.use((req, res, next) => {
// CRITICAL SECURITY FLAW: Reflects any untrusted Origin header
// Combined with credentials: true, completely dismantles the Same-Origin Policy!
const origin = req.headers.origin;
if (origin) {
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
}
next();
});
app.get("/api/account/data", (req, res) => {
res.json({ ssn: "123-45-6789", balance: 95000 });
});
// SECURE: Strict Explicit Origin Allowlist & Zero Credentials on Public APIs
const express = require("express");
const app = express();
const TRUSTED_ORIGINS = new Set([
"https://dashboard.example.com",
"https://admin.example.com"
]);
app.use((req, res, next) => {
const origin = req.headers.origin;
if (origin && TRUSTED_ORIGINS.has(origin)) {
// Only permit explicitly enumerated, trusted internal domains
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Vary", "Origin");
} else {
// Untrusted origins receive NO access control permission headers
res.removeHeader("Access-Control-Allow-Origin");
}
next();
});
Origin header into Access-Control-Allow-Origin.Access-Control-Allow-Origin: * alongside Access-Control-Allow-Credentials: true.