What does CORS prevent?

What does CORS do? Why use CORS to limit specific domains to call your API? Maybe it prevents a few bad things...

CORS = Cross-Origin Resource Sharing

Some of the software engineers I have met (not a lot, I hope) casually add CORS in the API because it wasn't working when their frontend tries to call it. They google around the error and just add CORS with wildcard value '*', allowing all domains to now able to access the API from the browser.

Once it works, CORS settings is left forgotten and people move on.

Here, we take a moment to appreciates CORS a little bit more.

Allowing all domain to be able to call your API is dangerous. What CORS does is allow you to configure only the websites with specific domains can call your API. By doing so, it prevents a few things:

First, it prevents the API from being able to access by any random website.

Imagine the scenaio where wildcard '*' is used for CORS setting. Alice has an API which is only meant for Alice's website, Bob creates another website that consumes Alice's API which has some expensive operations. Now, Bob can abuse Alice's API, the browsers from Bob's users will then be executing API calls to Alice's API server, taking up resources - one variant of DDoS attack.

Second, it prevents something more subtle - malicious hackers trying to steal users' credential information. Here is how it works.

Imagine; Alice's API sets some cookies on the browser, Bob's Web UI now executes GET call to Alice's API by setting 'withCredentials' value to true.

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://alice-api.com/', true);
xhr.withCredentials = true;
xhr.send(null);

Now this request will sent back the cookies previously set by alice-api on the user's browser. The cookies can contain user's credential information used to authenticate to alice-api.

On the server side, there is something called Access-Control-Allow-Credentials header,  of which the value can be either true or the header is ommited.  It goes hand-in-hand with CORS Access-Control-Allow-Origin header.

Almost all the CORS library support setting Access-Control-Allow-Credentials header value which is either true or false (which means the header is not included in the response at all). If the header value is set to true, it allows JavaScript of whatever web application making API call can see the response. Otherwise, the app (JavaScript) can't see the response value. This is important because anything that requires user's credential is considered important and equally sensitive as user's credentials.

Scenario 1: Intended Website to API

This is the example response when Access-Control-Allow-Credentials value is set to true, and Access-Control-Allow-Origin value is set to intended website domain.

In this case, Origin is properly set to the website's domain. Everything works fine. Cookies are carried over and sent back to the API, which in turn also sends back the response, which JavaScript can access.

Now another example where Access-Control-Allow-Credentials is not included and Access-Control-Allow-Origin value is set to the intended website domain.

Now JavaScript cannot see the response and fails with the reason "CORS missing allow credentials" although the cookies are being sent to the API server, too.

Scenario 2: Malicious Website to API

This is the example response when Access-Control-Allow-Credentials value is set to true, and Access-Control-Allow-Origin value is set to intended domain.

It fails with "CORS Allow Origin Not Matching Origin" error.

Take note that cookies which belong to Alice's Website, set by Alice's API, are still being sent over in the request as shown in the screenshot below.

There you have it. Bob's website simply tries to execute GET API from Alice API. It sends legitimate cookies from Alice's website set by Alice's API back to the API and tries to get hold of the sensitive information from the response.
But, thanks to CORS,  "Allow Origin Not Matching Origin" error prevents user from Bob's malicious website faking and stealing sensitive information, or even doing some destructive action.

Another thing that makes CORS more secure is we can't set Access-Control-Allow-Origin to '*' wildcard when Access-Control-Allow-Credentials value is set to true (quote below), so that malicious websites can't steal user's protected data just because some engineer accidentally sets '*' as  Access-Control-Allow-Origin value.

Note: When responding to a credentialed requests request, the server must specify an origin in the value of the Access-Control-Allow-Origin header, instead of specifying the "*" wildcard.

Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

I hope this helps understand CORS a little bit more.

PS: CORS is only for browser. It doesn't do anything if you are using curl command or any other manual tools.