Seen the above before? Probably… and probably quite often…
There are countless articles explaining how to fix the error mentioned above, but what exactly is this “Cross-Origin Resource Sharing” (CORS) thing, and why does it even exist?
Why???
Let's start by addressing why CORS exists through a scenario and how it would play out over time.
Imagine this: you log into bank.com, your banking service. After logging in, a “session cookie” is stored in your browser. (Session cookies basically inform the server behind bank.com that your browser is now logged into your account). All future requests to bank.com will now include this cookie, allowing the server to respond appropriately, knowing you are logged in. Now, you decide to check your mailbox and encounter a suspicious email. You click the link inside, which sends you to attack.com. This website sends a request to bank.com to get your banking details. Keep in mind that bank.com still considers you logged in because of the session cookie stored in your browser. For the servers behind bank.com, it looks like you requested your banking details normally, so they send them back. Consequently, attack.com gains access to your details and stores them maliciously.
People realized this was a major security issue, so browsers adopted a Same-Origin Policy (SOP). Under SOP, if your browser notices you are trying to make requests to bank.com from anywhere other than bank.com, they will be blocked. This is crucial to understand — it is a browser-based policy. Bank.com cannot determine where a request originates from and cannot protect against attacks like CSRF on its own. The browser intervenes and only sends requests for the same origins (scheme + domain name + port, such as https://foo.com:4000, http://bar.org:3000, etc.).
While this was effective, it was also highly restrictive. Public APIs wouldn’t work at all unless a proxy solution was used.
CSRF
Servers can somewhat determine where a request originates from. There is an “Origin” header that should be included in requests, indicating the origin that made the request. For example, in the scenario above, the request would include an Origin header like this.
Request to -----> bank.com { Headers: { Origin: http://attack.com } }
In theory, bank.com should be verifying the Origin header to ensure it only responds to legitimate requests, which it typically does. However, the Same-Origin Policy (SOP) can be quite restrictive. This is where Cross-Origin Resource Sharing (CORS) comes into play.
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). 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 intended 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.
The browser facilitates this process. If something is incorrect, you’ll encounter a CORS error.
Handling Complex Requests
What if the request lacks the Origin header? What if it includes numerous other headers or uses non-standard HTTP methods?
In these situations, the handling of CORS becomes more complex, as it is no longer a "simple request." This is where "preflight" requests come into play.
Preflight Requests
For requests that might modify data on the server—those using HTTP methods like PUT or DELETE, or those that include non-standard headers—browsers will first send a "preflight" 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 describing 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 and Access-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, resulting in a CORS-related error.
Conclusion
Hopefully, you now have a better understanding of CORS. It's essential to recognize that CORS is a browser policy, and your server must be configured to comply with it. It's in place to protect users. If you use a compliant browser like Chrome, you’re generally safer when clicking links, though caution is still advised. However, if you use a non-standard browser that doesn’t adhere to these policies, the protection CORS offers might not apply, so always be mindful of the software you use!