One of the first features I worked on at my job as a software engineer was to provide support for CORS requests in our product, the Zenko CloudServer. What is CORS? I thought. What is it used for? Before I could start working on this feature, I had to build up a knowledge of what CORS was in the first place.
“CORS” stands for Cross-Origin Resource Sharing. It allows you to make requests from one website to another website in the browser, which is normally prohibited by another browser policy called the Same-Origin Policy (SOP).
Recently, the topic of CORS resurfaced at work, when one of our contractors was having problems making a request to the CloudServer API in a browser. CORS was the answer to this problem, and since it’s a problem that other junior developers may encounter when working with APIs in the browser, I thought I would share what I’ve learned in case it might be helpful. 😊
Disclaimer: I have gathered the information I’m presenting here from an amalgam of resources already out there on the web (linked at the bottom of this post). Please don’t hesitate to correct or clarify misleading information if you spot a misunderstanding!
To understand CORS, one must understand Same-Origin Policy.
At the crux of it, CORS and SOP are both browser policies that have developed in response to issues of browser security and vulnerabilities.
The specific browser vulnerability that Same Origin Policy is meant to address is called “cross-site request forgery” (CSRF, or alternatively XSRF, don’t you love all these acronyms?).
Before browsers implemented SOP, malicious websites were able to exploit cookies (see: what are cookies? here) stored by your browser to make unauthorized requests to other domains. Some of these unauthorized requests could do things like make purchases, delete user information, fetch sensitive data, etc. 😱
As an example, you might go to a banking website and provide some credentials to log into your account. Your username is stored in a secure browser cookie for a certain period of time so the bank can tell you are still logged in instead of having you login another time with each page you access. That’s nice!
The easy fix was for browsers to detect when a request is made from one website to another and prevent the response from being readable. This is the Same-Origin Policy.
Wait a minute, how can I load images from another website on my own then?
You might know that loading images from other sites and embedding other content on your webpage requires browsers to send GET requests to fetch data from another web server, and haven’t had any problems doing this.
Unlike AJAX requests, browsers do allow embedding most content from other websites, such as images, script tags, css, etc, by default. Some possible exceptions are iframes, depending on the site’s configuration, and fonts, depending on the browser.
This does leave an avenue of attack open for CSRF/XSRF attacks, since you could use an <img src=‘…’/> to make a malicious GET request to another website rather than loading an image. This means servers have to do some additional work on their end to implement security, by using CSRF tokens or transaction tickets, if GET requests can result in any unwanted side effects or revealing confidential information. Read more here: https://stackoverflow.com/a/29167709 and https://stackoverflow.com/a/27294846 (where CRSF Tokens is bolded).
So what if I want to make an AJAX request to an outside API in a browser script?
Vice versa, what if you are developing an API and you want to allow people to make AJAX (or newer web APIs like Fetch) requests to your API in the browser? Or more simply, you want to make sure anyone can access a font you are sharing from your website, regardless of their browser?
At last, we’ve arrived at CORS.
CORS allows servers to specify certain trusted ‘origins’ they are willing to permit requests from. Origins are defined as the combination of protocol (http or https), host (a domain like www.example.com or an IP address) and port. Browsers which implement the CORS policy will include a HTTP header called ‘Origin’ in requests made with AJAX, including above information the value.
To instruct the browser to expose server responses to a HTTP requests from certain origin, the web server must respond to the request with an additional HTTP response header, ‘Access-Control-Allow-Origin: <origin>’. Alternatively, the web server may expose its responses to all origins by specifying a value of ‘*’, e.g. ‘Access-Control-Allow-Origin: *’.
This might be sufficient for simple GET, HEAD, or POST requests without any special http headers. However, for DELETE and PUT request, ‘unsafe’ requests which may impact the server’s data, or GET, HEAD and POST requests with customized headers, the browser will send a “preflight” request to find out the CORS result prior to sending the actual request (only if the preflight response determines access is permitted).
The preflight request is an OPTIONS request made to the same HTTP path as the actual request, with a couple of HTTP headers:
- Origin — The origin header that would be included with the actual request being made by the website.
- Access-Control-Request-Method — The method of the actual request being made by the website.
- Access-Control-Request-Headers — A comma-separated list of headers that would be included in the actual request.
Web servers that wish to support CORS requests must respond to preflight requests with the following HTTP headers:
- Access-Control-Allow-Origin — The whitelisted origin, or ‘*’
- Access-Control-Allow-Methods — A comma-separated list of HTTP methods the web server wishes to permit for cross-origin requests
- Access-Control-Allow-Headers — A comma-separated list of HTTP headers the web server wishes to permit for cross-origin requests
If any of the information in the response headers does not match the actual parameters of the request, the browser will not send the actual request, preventing unwanted side-effects from the server receiving the cross-origin request. (After all, including ‘Access-Control-Allow-Origin’ in the response to any of the other requests does not prevent those requests from being made to the server; it only prevents the browser from displaying the response.)
There are more CORS headers the server may respond with, including ‘Access-Control-Expose-Headers’, ‘Access-Control-Max-Age’, and ‘Access-Control-Allow-Credentials’. Rather than go into too much detail, I would encourage you to take a look at MDN’s detailed article about CORS (examples of HTTP requests/responses included!) and the meaning of each header.
But what if I am using an API, not developing one?
You may notice all of the above is related to the response that must be sent from the server side. Typically, your browser or browser SDK will send the Origin request header and create preflight requests for you when you are making an AJAX request.
Instead, you may need to configure the service you are using to permit your origin, a list of origins, or all origins. As an example, the Zenko CloudServer, which follows Amazon’s S3 Protocol, exposes an API method (Put Bucket CORS) you can use to configure a bucket with a CORS configuration with CORS rules which are used to determine server responses to CORS requests. (This is the feature I worked on that I mentioned at the beginning of the article.)
I might update this article later with a guide to using Put Bucket CORS with Zenko CloudServer in case the examples are helpful, but since this article is targeted at junior developers, I thought it would be most helpful to understand how CORS as a whole works in any case.
I hope this broad overview helped make CORS make a little more sense than it might have if you dug into the topic without it. Honestly, it is meant more as a primer than a complete resource, and I encourage you to look at the resources listed in “Further Reading” below to finish painting a complete picture of CORS.
Feel free to leave your feedback as to whether it helped you or if anything could be clarified. In the meantime, this summary by Julia Evans may be appropriate:
MDN Article on CORS
MDN Article on Same-Origin Policy
CORS — Wikipedia
Cross-Site Request Forgery — Wikipedia
Same-Origin Policy — Wikipedia
same-origin policy and CORS — what’s the point? (StackOverflow)
Same origin Policy and CORS (Cross-origin resource sharing) (StackOverflow)
Understanding and using CORS— Restlet
Cross-domain Ajax with Cross-Origin Resource Sharing — NCZOnline