A CORS misconfiguration that combines Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true allows any website to make authenticated cross-origin API calls using the visitor's session cookies — a variant of CSRF. CWE-942 (Permissive Cross-domain Policy) and OWASP A05 (Security Misconfiguration) cover this. NIST 800-53 SC-7 requires boundary protection. Substring origin validation (e.g., origin.includes('yoursite.com')) is trivially bypassed with a domain like evil-yoursite.com. The wildcard origin tells browsers to skip the same-origin check entirely, making your API accessible to any script on the internet.
Low because CORS misconfiguration requires an attacker to exploit it from a browser context, but wildcard-plus-credentials is a direct bypass of the browser's same-origin protection.
Use an explicit origin allowlist with exact string equality — not substring matching — and vary the response on Origin:
const ALLOWED_ORIGINS = [
'https://yoursite.com',
'https://www.yoursite.com',
...(process.env.NODE_ENV === 'development' ? ['http://localhost:3000'] : []),
]
export function corsHeaders(origin: string | null) {
const allowed = origin && ALLOWED_ORIGINS.includes(origin)
? origin
: ALLOWED_ORIGINS[0]!
return {
'Access-Control-Allow-Origin': allowed,
'Access-Control-Allow-Credentials': 'true',
'Vary': 'Origin', // Required for correct CDN caching
}
}
Never set Access-Control-Allow-Origin: * alongside Access-Control-Allow-Credentials: true — browsers will ignore credentials with a wildcard origin, breaking auth anyway.
ID: security-hardening.secrets-config.cors-restrictive
Severity: low
What to look for: List all CORS configuration locations (middleware, framework config, API route headers). For each, check CORS middleware or headers configuration. Look for Access-Control-Allow-Origin: * (wildcard) combined with Access-Control-Allow-Credentials: true. Check how allowed origins are constructed — a regex or string comparison that can be bypassed (e.g., checking if origin contains yoursite.com, which would also match evil-yoursite.com).
Pass criteria: CORS headers specify an explicit allowlist of trusted origins. If wildcard is used, credentials are not included. Origins are compared with exact string equality, not substring matching — fewer than 1 wildcard (*) origin in production CORS configuration (meaning 0%). Report: "X CORS config locations found, all Y use restrictive origins."
Fail criteria: Access-Control-Allow-Origin: * with credentials. Wildcard subdomains without verifying the full origin. Origin validation using substring matching that can be bypassed.
Skip (N/A) when: The API is not accessed from browsers (server-to-server only) and has no browser clients.
Cross-reference: The csrf-protection check covers request forgery which CORS alone cannot prevent.
Detail on fail: "Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true — any site can make authenticated cross-origin requests" or "Origin validated with includes() check — evil-yoursite.com would bypass the check for yoursite.com"
Remediation: Use an explicit origin allowlist with exact matching:
const ALLOWED_ORIGINS = [
'https://yoursite.com',
'https://www.yoursite.com',
process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '',
].filter(Boolean)
export function corsHeaders(origin: string | null) {
const allowed = origin && ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0]!
return {
'Access-Control-Allow-Origin': allowed,
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Vary': 'Origin', // Important: vary on Origin for correct caching
}
}