Cross-Origin Resource Sharing (CORS): What is it? How does it work?
CORS is a security mechanism that allows web applications to make cross-origin requests.
Cross-origin requests are requests that are made from one domain to another domain.
By default, web browsers prevent cross-origin requests for security reasons. However, CORS allows web applications to make cross-origin requests by adding specific headers to the HTTP response.
CORS relaxes the same-origin policy to enable web applications interact with each other in a secure manner, while not protecting web applications from specific attacks.
Browser enforces CORS to XMLHttpRequest
, Fetch API
, Web Fonts
and a few others.
What’s an Origin
An origin is defined by the scheme, host, and port of a URL. For example, the origin of https://example.com:8080
is https://example.com:8080
. it’s basically the value you see in the address bar of your browser.
What’s the Same-Origin Policy
The same-origin policy is a security feature of web browsers that prevents web applications from making cross-origin requests.
The same-origin policy restricts web applications to only make requests to the same origin as the web application. This helps prevent attackers from making unauthorized requests to a web application.
What’s a Cross-Origin Request
A cross-origin request is a request that is made from one origin to another origin. For example, if you have a web application running on https://example.com
that makes a request using one of the mentioned elements above for instance a Fetch to https://api.foo.com
, this is a cross-origin request.
How Does CORS Work
When a web application makes a cross-origin request, the browser sends a preflight request to the server to check if the server allows the request.
The preflight request is an HTTP OPTIONS request that includes the Origin
header. The server responds with the Access-Control-Allow-Origin
header, which specifies which origins are allowed to make cross-origin requests.
If the server allows the request, the browser sends the actual request. If the server does not allow the request, the browser blocks the request.
If the request is a simple request, the browser sends the actual request directly without sending a preflight request. The preflight request is to check if the actual request is safe to send and does not have side effects.
A simple request is a request that meets the following criteria
- The request method is GET, HEAD, or POST.
- Apart from the headers that the browser always sends, the only headers that the request can include are
Accept
,Accept-Language
,Content-Language
,Content-Type
andRange
- The
Content-Type
header isapplication/x-www-form-urlencoded
,multipart/form-data
, ortext/plain
.
Note that not sending a preflight request does not mean that the CORS policy is not enforced. The browser still enforces the CORS policy for simple requests.
I said earlier that since a simple request is safe, the browser does not send a preflight request. However, you saw that a request with a POST method could be a simple request and we know that a POST request could change state of server. This is to allow HTML forms to submit data to a different origin. CORS is not meant to enforce restrictions but to relax the same-origin policy.
a Simple Application to Demonstrate CORS
Let’s setup a simple application to demonstrate CORS. We will have two flask applications, one running on http://devbox:3000
and the other running on http://devbox:4000
.
We’re using linux hosts file to serve the application under a domain name. hosts file is a handy way to map domain names to IP addresses. In a production environment, you would use a DNS server to map domain names to IP addresses. It’s not needed for the demonstration tho.
|
|
devbox 1
|
|
|
|
It’s accessible at http://devbox:3000/test/
devbox 2
|
|
|
|
|
|
Now if you open http://devbox:4000
in your browser, you will see the following error in the console
Access to fetch at 'http://devbox:3000/test/' from origin 'http://devbox:4000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
is present on the requested resource. If an opaque response serves your needs,
set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This error occurs because the browser blocks the request from http://devbox:4000
to http://devbox:3000
due to the same-origin policy. To fix this error, we need to enable CORS on the server running on http://devbox:3000
.
|
|
It should work now.
If withCredentials
is set to true
in the request, the server must also include the Access-Control-Allow-Credentials
header in the response with the value true
.
When you visit a website, the browser always sends cookies that are associated with the website to the server. This allows the website to identify you and provide personalized content.
However, when you make a cross-origin request, the browser does not send cookies and HTTP authentication information by default. You can enable this behavior by setting the withCredentials
flag to true
in the request.
Browser also checks other headers like Access-Control-Allow-Methods
, Access-Control-Allow-Headers
, Access-Control-Max-Age
and Access-Control-Expose-Headers
to determine if the request is allowed. this depends on the request method and headers.
The previous request didn’t trigger a preflight request because it was a simple request. If the request was a non-simple request, the browser would have sent a preflight request to the server to check if the server allows the request.
Let’s make the request a non-simple request by adding a custom header to the request.
|
|
Now if you open http://devbox:4000
in your browser, you should see the following error in the console
|
|
Unlike the simple request, the browser sends a preflight request to the server to check if the server allows the request. The server must respond with the Access-Control-Allow-Origin
header to allow the request.
To fix the issue, we need to update the application to respond the preflight request which is an HTTP OPTIONS request.
|
|
Let’s try again.
Access to fetch at 'http://devbox:3000/test/' from origin 'http://devbox:4000'
has been blocked by CORS policy: Request header field x-custom-header is not
allowed by Access-Control-Allow-Headers in preflight response.
We need to update the server to allow the X-Custom-Header
header in the preflight response.
|
|
Note that Access-Control-Allow-Headers
should be present in both the preflight response and the actual response.
What Attacks Does CORS Protect Against
CORS does not protect against specific attacks. It is a security mechanism that allows web applications to make cross-origin requests. However, CORS can help prevent certain types of attacks, such as cross-site request forgery (CSRF) attacks.
By restricting which origins are allowed to make cross-origin requests, CORS can help prevent attackers from making unauthorized requests to a web application.
Check out the MDN documentation for more information.