Totally agree. Something about CORS and the resources out there makes newcomers think that it's something the client has to do differently. I thought this when first doing cross-origin stuff, and thought it was just me until my dad (programmer for 30 years) got stuck on it the exact same way.
Also, the author makes a good point that `Access-Control-Allow-Origin: *` is pretty dangerous. I hadn't really worried about it in the past, because "I don't care who calls my api, they aren't authenticated". But if malicious client code got a hold of a user's session (using XSS or what have you), I'd be open to them steering my user's session and doing horrible things. Definitely going to review my current projects with this in mind.
Edit: of course, if I have an XSS vulnerability, they'd be able to do that from my domain anyway, so CORS doesn't 100% fix the problem. XSS is bad.
> But if malicious client code got a hold of a user's session (using XSS or what have you), I'd be open to them steering my user's session
I think the point of this stuff is that, if the user's session is stored in a cookie, and malicious javascript can send HTTP requests to your site (that will use whatever cookie is already present) and read the responses -- they can steer your session without needing to get a hold of a user's session using XSS or anything else.
They already HAVE a hold of the user's session, because they can control the browser, which does.
The point of cross-origin restrictions is to prevent JS loaded from one site from steering the user's session on a different site. (Maybe among other sorts of attacks).
CORS lets you disable those protections. `Access-Control-Allow-Origin: ` disables them entirely.
I could have some of this wrong, I find this stuff confusing too.
But I believe they do not need to "get a hold of a user's session (using XSS or what have you)" in order to steer a user's session under `Access-Control-Allow-Origin: `
I guess that's what I meant; XSS would give them the user's session implicitly on my domain, regardless of CORS. CORS prevents them from using that from another domain, which is valuable, but moot if you already have a breach.
OK, I think I was confused about what "XSS" means or how you meant it or I was thinking about it differently.
The important point though: You don't need any pre-existing vulnerability on your site in order for "Access-Control-Allow-Origin: *" to create a vulnerability.
This stuff is sure is confusing to talk/think about though.
> Something about CORS and the resources out there makes newcomers think that it's something the client has to do differently
I think that "something" is the fact that the client does the blocking. Generally the servers are perfectly willing to provide the data. Heck, if you sniff the packets, you could even look at the data blocked by CORS (assuming you're working around TLS). It's especially confusing at first that tools like curl work no problem, while browser block everything.
Yes, that's definitely a big part of it. Test it all with curl or your language's http client, then put it in the browser and it breaks? Must be the client's fault! Which it sorta is. But only because it's a client that doesn't belong to you — you're controlling it on the user's behalf. It doesn't help that when you're developing on your own machine, you're user and the developer, and you of course trust yourself.
What would you use instead of that? Suppose you're doing something like client-side Blazor that calls to a WebAPI project. How can you improve security by restricting the origin of access requests when your client is open to the public?
Access to an API should be managed by a proper authentication or token validation scheme. However, protecting your users' authenticated API sessions, which are presumably initialized by the forementioned authentication scheme, is what CORS enables.
CORS, when implemented correctly, ensures that a session is not hijacked by a malicious website's JavaScript in order to call your API in the context of the session (effectively masquerading as the user who authenticated with your API). This scenario assumes that there is an authentication session cookie, tied to your API domain, that the browser would pass along with any request to your API domain (of course there are SameSite cookie and third party cookie blockers that can mitigate these situations as well, but perhaps "trusted" cross domain requests are desired in this use case)
With CORS allowing traffic from anywhere on the web, you can't reliably trust that the authenticated sessions to your API are not being used in phishing / side channel attacks: I discover your are authenticated on site on foo.example.com and I send you a link to my website, evil.com. Evil.com includes JavaScript to request data from an API on foo.example.com. Your browser executes the JavaScript and makes the request and gets a response payload, and since I'm a jerk I then post that same payload to my own endpoint on evil.com to capture the data.
Of course this all assumes that you WANT your API to be accessible cross origin. If you provide an API as a product this is common, since it allows other developers to build web apps against your API. If that is not a use case, then same origin policy (and no CORS headers from the API server) is sufficient to prevent malicious domains from doing bad stuff with your users' authenticated sessions.
I'm not familiar with Blazor or WebAPI, but if you're not expecting requests from client-side javascript specifically, you can just use `Access-Control-Allow-Origin: null`, since non-browser clients don't respect CORS anyway.
If by "open to the public" you mean "open to requests from any client domain", CORS isn't going to help. In that case I'd probably have api clients pre-register a whitelist of domains that they're planning to make client-side requests from, so you can check the domain against the whitelist and dyanamically build your allow-origin header.
Can you point me to some documentation for dynamically allow-origin header? I’m working on open sourcing our frontend and so if users have their own frontend on their own domain, how do we allow those calls to our backend with CORS? If we turn CORS off, is this a security issue? From the frontend, we send a JWT with the header and check this for protected routes on the backend.
Dynamic allow-origin sounds magical, but is really straightforward. You just look at the `Origin` header of a request (e.g., in express `req.headers['Origin']`), compare it against your database of whitelisted origins, and if it's in there, return it as the value of `Access-Control-Allow-Header`.
If you don't have any relationship with the folks using your frontend, I'd just "turn it off", that is, use "Access-Control-Allow-Origin: *". It's a security issue only insofar as you don't trust the third party that owns the web frontend to handle their users' data securely, either by introducing their own security vulnerabilities, or by hijacking users' sessions themselves. The big question I think is whether the third party's users are your users too, in which case you're responsible to vet the third party to protect your users. If you're just a backend for whatever-the-heck, just make sure you have a good terms of service for the api so you're not assuming responsibility for other people's mistakes/malice.
It's not fair to call Dynamic Allow-Origin simple. This is super tricky and nonstandard usage that is only necessary to workaround the fact that browsers do not support multiple values on the Allow-Origin header even though the spec allows it.
That said, yes, when you want to allow multiple origins, reflecting the Origin request header in the Allow-Origin response header is the only solution that works. (Note however, that sometimes the Origin header is not present, an additional difficulty.)
Realistically, Dynamic Allow-Origin is the only way when you want to add more than a few allowed Origins. You wouldn't want to send back a 10kb header detailing all the clients of your service, would you?
You shouldn’t reflect the origin unless it matches your whitelist. If you wanted to allow all you would just use *. If its not allowed you should return invalid headers instead. Thats why its dynamic
The single-value constraint seems like a feature rather than a bug; if you included your full list of whitelisted domains every time, not only would your HTTP header size be unnecessarily heavy, but you'd be leaking private details about who else is using your service. This isn't an inherent problem, but it could give an attacker some ideas of who to target.
Yes so the users will be users of ours. The users won't have their own users per se, but will be able to customize the app. So if I understand correctly, I can turn off CORS, have each user that wants their own backend URL to enter this URL in our system, we whitelist this URL and check for it when a request is made?
Depends what you are trying to do. If you are offering an API for third parties to use, Access-Control-Allow-Origin: * is pretty appropriate. It basically communicates "I don't care where you call me from". Same with content on a CDN (fonts, css, etc.). Of course serving executable content like that is a bad idea; but, even so, people seem to trust certain domains with things like jquery.
WIth images, fonts, or other static content the risk is arguably fairly limited except for the fact that browsers tend to have all sorts of nasty bugs with malicious content overflowing buffers thus leading to arbitrary code execution.
But of course the elephant in the room is that doing things with headers adds a lot of friction for developers. Now their simple application has a devops component. E.g. modifying nginx to add that header or trying to make AWS CloudFront forward CORS headers is just a royal pain in the ass. It's also very fragile because these things break easily and are rarely covered by integration tests because these things tend to be specific to infrastructure and environment.
| Also, the author makes a good point that `Access-Control-Allow-Origin: *` is pretty dangerous.
But what is the alternative when you have a script that is going to be deployed on multiple sites that you do not control? Which origin do you specify? This is the scenario which always trips me up and results in kludgy workarounds.
I think the generally accepted solution to this is to set the allowed origin dynamically (IIRC nginx can do this) by looking at the request host header on the options request. If the origin is in some allowed list then you return that origin in `Access-Control-Allow-Origin`
I _think_ that is an the appropriate use for `Access-Control-Allow-Origin: `.
It would be up to you that only the URL for such scripts (not your entire site) have `Access-Control-Allow-Origin: ` , and to make sure that there is nothing malicious JS can do with `Access-Control-Allow-Origin: *` at those particular URLs.
Which is confusing to figure out, it's true, because the whole thing is confusing, indeed.
The company I work for provides embeddable JavaScript "web widgets". Our customers are companies, and their customers are consumers (we are business to business to consumer company). We host somewhat personal data specific to those consumers, and as such, the data should only be accessible by those consumers.
So, we provide the data API that these web widgets communicate with, and the web widgets themselves can be embedded on our customers' own websites (some-company.com). However, the customer can decide exactly which hostnames can embed the widget (through a control panel we provide), and these hostnames ultimately become the `Access-Control-Allow-Origin` value we provide with every API response. Perhaps they want it on foo.some-company.com or bar.some-company.com -- it's really up to our customer where they want these widgets to be embedded.
By doing this, the customer knows that no other website can host these widgets, thus exposing their consumers' data to a phishing attack as I outline here: https://news.ycombinator.com/item?id=20405169
Also, the author makes a good point that `Access-Control-Allow-Origin: *` is pretty dangerous. I hadn't really worried about it in the past, because "I don't care who calls my api, they aren't authenticated". But if malicious client code got a hold of a user's session (using XSS or what have you), I'd be open to them steering my user's session and doing horrible things. Definitely going to review my current projects with this in mind.
Edit: of course, if I have an XSS vulnerability, they'd be able to do that from my domain anyway, so CORS doesn't 100% fix the problem. XSS is bad.