Difference between `Access-Control-Allow-Origin: *` (wildcard) and specific origins
jeanid last edited by
I have a mostly public API with some parts of it "credentialed" behind cookies, similarly to e.g. how WordPress' REST API works. (In our case, it's a GraphQL API but that shouldn't matter.)
I want to enable CORS for it and am considering two options:
Access-Control-Allow-Origin: <dynamically return the incoming `Origin` header> Access-Control-Allow-Credentials: true
(Plus other headers like
Access-Control-Allow-Methodsin both cases.)
https://evil.com, all should be OK:
Access-Control-Allow-Origin: https://evil.com Access-Control-Allow-Credentials: true
There's no real problem – the evil site will be able to read the public parts of the API (which they can already do via
curl, for example) but they won't be able to abuse a browser of a specific user that would be "logged in" to the API – cookies for our "site" will not be sent.
WordPress also uses Option 1 (for many years) and it's secure enough – see e.g. this Trac ticket.
Still, I wonder why not use a wildcard – it's simpler code (no need to dynamically read the
Originheader), no need to worry about setting related headers properly (
Vary: Origin) etc. The wildcard just seems simpler.
So my question is, why would I choose Option 1 for our API? What useful scenario does it unlock that wildcard prevents?
Option 2 simply doesn't work, and option 1 is insecure.
Simply hardcode the origin that you trust in the CORS response header.
As Stephen Ullrich pointed out in his comment, Option 2 simply doesn't work because the Fetch standard (which defines how CORS works) instructs browsers to reject this combination of headers. See this relevant passage of the MDN Web Docs about CORS:
When responding to a credentialed request, the server must specify an origin in the value of the
Access-Control-Allow-Originheader, instead of specifying the "
As for Option 1, it was very insecure before major browsers changed the
SameSitedefault value to
Lax(and many browsers still haven't made that change). But even if the session-identifying cookie is set with
SameSite=Strictand all yours users have modern browsers, Option 1 remains quite insecure. You write
There's no real problem – the evil site [...] won't be able to abuse a browser of a specific user that would be "logged in" to the API – cookies for our "site" will not be sent.
Do not misconstrue the
SameSiteattribute as a license to blindly accept any origin from a credentialed CORS request! The problem is that
SameSiteonly applies to cross-site requests, not to all cross-origin requests. To convince yourself that this subtlety matters, see
- https://web.dev/same-site-same-origin/, and
Can you guarantee that the subdomains (or sibling domains) of the origin that sets the session-identifying cookie will never have any XSS or HTML-injection vulnerability, or that they won't ever be taken over by some malicious actor? If the answer is "no" (and it most likely is "no"), I would strongly advise against Option 1.