Vulnerabilities
CORS Wildcard vs Allowlist for Public APIs
September 30, 2022
CORS wildcard vs allowlist is a trust-boundary choice for browser-called APIs, not a performance tweak. Access-Control-Allow-Origin: * is acceptable only for responses that are meant to be read by any website and that do not ride on cookies or client certificates. If the API uses credentials, reflect an exact allowlisted origin or reject the request. Wildcard plus Allow-Credentials is invalid in browsers and is a policy smell even when clients refuse it. RoastMyOpsec's app-mode CORS check sends a bounded GET/OPTIONS with an untrusted Origin — no credential stuffing, no authenticated abuse.
The practical difference
CORS is a browser rule. Other websites' JavaScript may not read your API response unless you opt in. Non-browser clients ignore CORS entirely, so it is not a substitute for authentication.
A wildcard says 'any origin may read this.' An allowlist says 'only these HTTPS origins may.' Reflecting whatever Origin arrived is usually worse than a static * because it looks like a tailored yes to an attacker site.
How to choose based on the API
Start with whether the response contains anything a stranger's page should not see. Public marketing JSON can often use *. Account, billing, and admin APIs cannot. If the browser will send cookies, you need Allow-Credentials and a specific origin — never *.
| API job | ACA-Origin | Credentials | Takeaway |
|---|---|---|---|
| Public, no cookies | * or a short allowlist | Off | Wildcard is a product choice, not a footgun |
| Same-site SPA + API | Exact app origin | On only if you actually use cookies | Prefer same-origin and skip CORS entirely |
| Partner widget | Named partner origins | Usually off; tokens in Authorization instead | Do not copy the partner list onto admin routes |
| Reflect any Origin | Attacker-controlled | Especially bad if on | This is the classic miss |
When a wildcard still fails OPSEC
Wildcard on a JSON error body that includes stack traces or account ids leaks through any website that can trigger the request. Combine boring errors with a tight origin policy.
If you do not need cross-origin browser reads, omit CORS headers. Absence is safer than a generous default copied from a tutorial.
Common mistakes
The first mistake is Access-Control-Allow-Origin: * plus Access-Control-Allow-Credentials: true.
The second mistake is echoing the Origin header with no allowlist.
The third mistake is setting CORS on the marketing site and forgetting the API host that actually holds sessions.
Free audit the URL you own
RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.
Free audit nowFAQ
- Is Access-Control-Allow-Origin * always a vulnerability?
- No. It is appropriate for public, credential-free resources. It is a miss on APIs that return private data or that expect cookies.
- Does CORS replace API authentication?
- No. CORS only affects browsers. Authenticate and authorize every request as if any client on the internet can call the URL.
- How does RoastMyOpsec test CORS?
- App mode sends a bounded request with an untrusted Origin and reads ACAO / credentials behavior. It does not send your users' cookies or attempt account takeover.
Sources
Related guides
Vulnerabilities
CORS Credentials vs Wildcard OriginsSafety
Private Network Access vs CORS for Browser APIsVulnerabilities
crossdomain.xml vs CORS for Public Browser APIs