Array of origins including wildcard
See original GitHub issueHi,
While implementing some CORS for a backend that allows different origins depending on the environment (development, stage, production, etc.) I found out that the behaviour I was expecting for a wildcard origin as part of an array of origin was not matching the actual implementation. Let me show you some code:
const ORIGINS_WEBSITE_A = ["https://somewebsite.com", "https://some-live-preview-of-the-website-s3-bucket.com"];
const ORIGINS_WEBSITE_B = ["*"];
const cors = require("cors");
cors({
origin: [...ORIGINS_WEBSITE_A, ...ORIGINS_WEBSITE_B];
});
You can imagine that ORIGINS_WEBSITE_A
and ORIGINS_WEBSITE_B
change depending on the environment (the above values could be valid for a staging environment for example). And in development most likely we want both of them to be "*"
.
As a user of the library, I was expecting that "*"
takes preference and I would get any origin as valid. My surprise is that the code does strict string comparison when using an array value for the origin
option.
Of course, this problem can be solved outside the library in several ways:
- Use a regex like
.*
instead of"*"
because they would pass. - Replace array of origins with
"*"
if some element matches"*"
. - Many others.
I wonder why this design decision was taken at some point of time and if it’s something the library might want to support.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:8 (4 by maintainers)
Top GitHub Comments
I respectfully suggest that this change not be made.
cors({origin: ‘*’}) means send a literal *, not allow-list all origins
If someone writes
cors({origin: '*'})
, their server will send response headers ofAccess-Control-Allow-Origin: *
, regardless of the request’s origin.The discussion here and in #204 seems to be assuming that
cors({origin: '*'})
either already means, or should mean, reflect back the request’s origin as though it were in the CORS allow-list. This is not the case, as can be seen from the README section on Configuration Options:and the unit tests, which show
Access-Control-Allow-Origin: *
being sent with the default configuration.The double meaning of “allow any origin”
allow any origin could mean
Meaning 1: Client-side
Send Access-Control-Allow-Origin: * to the client, telling the client any origin is OK, for browsers as long as credentials are not sent, or it could mean
Meaning 2: Server-side
Reflect the request’s origin back to the client as the value of the Access-Control-Allow-Origin header.
A crucial difference is that with Meaning 1: Client-side, the browser will not allow credentialed requests.
The proposed change
It is being proposed in this Issue and in #204 that if someone writes
cors({origin: ['*']})
, their server should send response headers ofAccess-Control-Allow-Origin: <request origin>
, with<request origin>
replaced by the specific origin from the request.The justification is the claim that this would make
cors({origin: ['*']})
behave similarly tocors({origin: '*'})
, but it would not. As explained above,cors({origin: '*'})
sends a response header ofAccess-Control-Allow-Origin: *
.Reasons not to make the proposed change
There are other ways to accomplish allow-listing everything
As noted in this Issue, if you want to allow-list all origins, you can do so with
cors({origin: /.*/})
.A huge relaxation of security between cors({origin: ‘*’}) and cors({origin: [‘*’]}) is undesirable
Currently, if someone means to write
cors({origin: '*'})
, and they writecors({origin: ['*']})
, their site will probably be inaccessible in development. If the proposed change is made, then someone writingcors({origin: ['*']})
may unknowingly open their site to credentialed requests from any origin.It is argued in this Issue that
But given that if someone writes
cors({origin: '*'})
, they will be configuring their server to sendAccess-Control-Allow-Origin: *
, I think it’s more likely that they wanted to sendAccess-Control-Allow-Origin: *
, notAccess-Control-Allow-Origin: <request origin>
.*
has a special meaning in CORS, and it is not reflect back the request’s origin.‘*’ in an array of origins should be avoided
'*'
doesn’t make sense in a list–if you’re allowing any origin, why list other origins?Thank you @douglasnaphas.
cors({origin: /.*/})
saved me.