CORS Finally Explained — Simply
Seen the above before? Probably… and probably quite a lot…
There are millions of articles explaining how to fix the error above, but what exactly is this “Cross-Origin Resource Sharing” (CORS) thing, and why does it even exist?
Why???
Let's begin by first answering the question of why through a scenario and how it’d play out at different points in time.
Imagine this: you log into bank.com
, which is your banking service. After logging in, a “session cookie” is stored in your browser. (Session cookies basically tell the server behind bank.com
that your browser is now logged into your account). All future requests to bank.com
will now contain this cookie, and it can respond properly knowing you are logged in.
Ok, so now you decide to check your mailbox. You see a suspicious email, and of course, you decide to click the link inside which sends you to attack.com
. Next, this website sends a request to bank.com
to get your banking details. Keep in mind that bank.com
still thinks you are logged in because of that session cookie… that cookie is stored on your browser. For the servers behind bank.com
, it just looks like you requested your banking details normally, so it sends them back. Now attack.com
has access to these and stores them elsewhere maliciously.
People realized this was bad, so browsers adopted an SOP (Same-Origin Policy) where if your browser notices that you are trying to make requests to bank.com
from anywhere other than bank.com
they will be blocked. Now, this is a key thing to realize — this is a browser-based policy. bank.com
really has no way to tell where a request comes from, so it can’t protect much against attacks like CSRF. The browser you are using steps in and basically says that if you seem to be trying to request details for an origin (scheme + domain name + port, https//foo.com:4000, http//bar.org:3000, etc… Basically the URL), it will only send those requests for same origins.
Now, this was great and all, but it was incredibly limiting. I mean, public APIs wouldn’t work at all. You couldn’t request data from them unless you used some sort of proxy solution.
CSRF
Here’s the thing: servers can sorta tell where a request came from. There is the “Origin” header, which requests should have, showcasing what origin made a request. For instance, in the above example, the request would look something like this.
Request to -----> bank.com
{
Headers: { Origin: http://attack.com }
}
bank.com
in theory, should be checking this to make sure it only responds to requests where the origin makes sense. And it usually does, so SOP seems kinda limiting.
This is where CORS comes in.
CORS
When a web application from example.com
attempts to request resources from bank.com
, the browser automatically includes an Origin
header in the request, indicating where the request originates from (example.com
). Here's the crucial part: Instead of outright blocking such cross-origin requests under the SOP, bank.com
's server can inspect this Origin
header and decide whether to allow or deny the request based on its own CORS policy.
If bank.com
considers example.com
trustworthy or the resource being requested is meant to be publicly accessible, it can respond with specific CORS headers, such as Access-Control-Allow-Origin
, indicating which origins are permitted to access the resource. This header might be set to http://example.com
, explicitly allowing this origin, or *
for public resources that any origin can access.
Of course, the browser facilitates all this. If any of it is wrong, you’ll get that nasty error.
Now… what if the request doesn’t have the Origin header? What if it has a bunch of other headers and doesn’t use one of the basic HTTP methods?
In these situations, the handling of CORS becomes a bit more intricate as it is no longer a “simple request”. This is where the concept of "preflight" requests in CORS comes into play.
Preflight
For certain types of requests that could potentially modify data on the server—those that use HTTP methods like PUT, DELETE, or use headers that are not automatically included in every request—browsers will first send a "preflight" request before making the actual request. This preflight request is an HTTP OPTIONS request, and its purpose is to check with the server whether the actual request is safe to send.
The preflight request includes headers that describe the HTTP method and headers of the actual request. Here’s what happens next:
- Server Response: If the server supports the CORS policy and the actual request, it responds to the preflight request with headers indicating what methods and headers are allowed. This might include headers like
Access-Control-Allow-Methods
andAccess-Control-Allow-Headers
. - Browser Decision: Based on the server's response to the preflight request, the browser decides whether to proceed with the actual request. If the server's response indicates that the request is allowed, the browser sends it; if not, the browser blocks the request, and you'll see an error related to CORS.
Conclusion
Now, hopefully, you understand a little bit more about CORS. I think the most important thing to realize is this is all browser policy, and your server must be coded to comply with it. It is in place to keep you safe. If you are using Chrome, you shouldn’t worry as much about clicking the wrong links (of course, you still should worry a little :D). However, this isn’t a foolproof policy. If you use some 3rd party browser that doesn’t comply with standards, all this would be thrown away. That is why one must remain careful with what software they use!