Cross-Origin Resource Sharing (CORS) is a mechanism that enables web pages to request resources from a server hosted on another origin, where an origin is defined as a web server that differs in domain, port, or protocol from the one making the request.
By default, modern browsers restrict web pages from accessing resources if a request is sent to the server via a different port, domain, or protocol than that of the page. This security mechanism is called the Same-Origin Policy (SOP). The intent behind this policy is to prevent unauthorized access to various website resources.
However, in today's web technology landscape, it's quite common for web pages to send requests to servers hosted on different domains. To regulate it properly, CORS has been introduced. It controls both access to responses from an external source and whether the request can be sent at all.
Browsers regulate the process by reading server response headers. If the server does not send the required values in special CORS headers, browsers will either restrict the page's access to the server's response or blocks the request from being sent.
Here are the main CORS headers:
Access-Control-Allow-Origin
specifies which origins have permission to receive server's response as part of cross-domain requests. If the server response does not contain the Access-Control-Allow-Origin
header with the origin value that has initiated the cross-domain request, either the main request will be blocked or the responses won't be provided to the page.Access-Control-Allow-Credentials
determines whether credentials
(for example, cookies) are allowed within cross-domain requests. If user cookies are passed in the request, and the Access-Control-Allow-Credentials
value is false
, either the main request will be blocked or the page won't receive the response.Access-Control-Allow-Methods
defines which methods are allowed within cross-domain requests. If the response to the preflight request does not include the main request method in this header, it's blocked from being sent.Access-Control-Allow-Headers
defines which headers are allowed within cross-domain requests. If the main request contains a header that is not specified in Access-Control-Allow-Headers
, it's blocked from being sent.In CORS, there are two types of requests: simple and preflight. The request type determines which headers within the server response are required, whether the request will be blocked, or the web page won't receive the server response.
The following are simple scenarios for implementing both types of requests.
We consider a request as simple if:
GET
, HEAD
, or POST
method is used;Accept
, Accept-Language
, Content-Language
, Content-Type
, or Range
;Content-Type
header contains one of the following values: application/x-www-form-urlencoded
, multipart/form-data
, or text/plain
.Let's imagine that we need to send the GET
request containing the Content-Type
header with the text/plain
value. The request will be sent from the http://localhost:4000
page to the server located at http://localhost:4001
.
The request will be sent, and a respond with a valid status code will be received. However, since the ports of the web page and the server differ, the Same-Origin Policy won't be fulfilled, and the browser will restrict the page access to the response.
For the page to access the response, the server must return a valid Access-Control-Allow-Origin
CORS header with http://localhost:4000
. In this case, the browser grants the page access to the response content.
If the request includes any credentials
(like cookies or TLS certificates), and the server response doesn't contain the Access-Control-Allow-Credentials
header with the true
value, the browser will restrict the page access to the response.
If requests don't meet the criteria for a simple request, the browser will send a preflight request before sending the simple one to the server.
For example, if we replace the GET
method with DELETE
in the request, the browser will process it differently: the preflight request with the OPTIONS
method will be sent to the server before the main one. It will contain information about the main request, including its method, the address of the web page from which it was sent, and its headers.
The browser expects to receive the necessary CORS headers in response. However, there's an important difference: if the response to the preflight request does not fulfill the browser, the main request won't be sent.
As in the previous example, we expect to see the Access-Control-Allow-Origin
header with http://localhost:4000
in the response to the preflight request.
Other CORS headers play an important role as well. For example, if the Access-Control-Allow-Methods
header does not contain DELETE
, the main request won't be sent either.
There is one important detail to highlight when working with CORS. In addition to a specific host, the Access-Control-Allow-Origin
header can take the *
(wildcard) value. It authorizes any host to view the contents of the server response. This can be non-compliant behavior, especially if these resources are expected for web pages with a specific domain.
The same applies to the Access-Control-Allow-Credentials
header with the true
value. Use it carefully, especially if the value of the Access-Control-Allow-Origin
header is dynamically generated on the server rather than taken from a predefined whitelist. This can lead to significant security vulnerabilities.
Violating the principle of least privilege can expose web applications to security risks:
So, if the application is not deployed in a test environment or is not intended to serve as a public API, the Access-Control-Allow-Origin
header should be defined with a specific whitelisted value.
If you wish to learn more about the possible vulnerabilities that misconfigured CORS can lead to, the articles below offer helpful insights:
To delve deeper into the CORS theory, welcome to the following pages:
0