CORS configuration is restrictive
Why it matters
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.
Severity rationale
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.
Remediation
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.
Detection
-
ID:
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 withAccess-Control-Allow-Credentials: true. Check how allowed origins are constructed — a regex or string comparison that can be bypassed (e.g., checking if origin containsyoursite.com, which would also matchevil-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-protectioncheck 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 } }
External references
- cwe · CWE-942 — Permissive Cross-domain Policy with Untrusted Domains
- owasp:2021 · A05 — Security Misconfiguration
- nist:rev5 · SC-7 — Boundary Protection
Taxons
History
- 2026-04-18·v1.0.0·Initial import from security-hardening·automated