Cross-Origin Resource Sharing (CORS) tends to be a complex and often misunderstood concept, causing significant frustration for application developers when encountered as an error.
In the realm of contemporary micro-services and micro-applications, where front-end and back-end components are distinct entities, developers frequently find themselves grappling with a frustrating CORS error during the development phase. This troublesome issue can prove to be highly irritating, leading to substantial time wastage and hindering progress.
People often bypass this error by allowing requests from all origins by specifying a wildcard(*) in the backend API service. While this is a quick and easy fix, it could lead to security vulnerabilities in the production environment where real customer data is at risk! It is precisely to protect from this vulnerability that the browser throws the CORS error.
CORS, an acronym for Cross-Origin Resource Sharing, serves as a mechanism enabling browsers to permit resources, such as scripts, from one domain to access APIs hosted on another domain. This functionality is crucial for bypassing the constraints imposed by the Same Origin Policy, which was initially implemented by browsers to safeguard web resources against malicious users third parties.
The Same Origin Policy (SOP) is a security feature implemented by web browsers to restrict web pages from making requests to a domain different from the one that served the original web page. The policy aims to prevent potential security vulnerabilities that enable attacks such as Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF or XSRF).
Before the introduction of the Same Origin Policy, XSRF attacks were alarmingly simple to execute. These attacks involve a network request, typically in the form of GET or POST requests, that carries out an undesired action on a target site such as www.my-bank.com, initiated by a script from a different domain, like www.malicious-site.com. For instance, an unsuspecting user opening an email from a “malicious-site” could unknowingly trigger an action on their logged-in “my-bank” site, initiated by the email from malicious-site! The earlier absence of restrictions allowed any site to instigate actions on other sites by sending network requests to their servers.
While the Same Origin Policy serves as a crucial security measure for protecting data and servers, there are scenarios where its strict constraints become limiting. Given below are a few instances that highlight the need for flexibility in making cross-domain calls.
CORS (Cross-Origin Resource Sharing) provides a solution to the restrictions imposed by Same Origin Policy by allowing relaxations. It achieves this by allowing specific domains and methods (such as GET and POST) to access resources in a controlled and secure manner. This configuration is typically specified in the back-end, as seen in popular frameworks such as Express JS, where CORS restrictions are set using the cors middleware.
CORS essentially tells the user that the resource (API) you are trying to access belongs to a different origin from the one that is hosting this script. If this is required, either explicitly white list your origin (protocol, domain, port) or bypass the error using a proxy server if this is for development.
In simplified terms, the browser assesses the safety of transmitting a network request (such as an API call) to the server by considering the domain and headers. If the browser deems it safe, the request proceeds; otherwise, it initiates a check with the server before permitting the request. This verification process is known as a preflight request. During a preflight request, the browser first dispatches an “Options” request to the server, providing details about the intended request, and then seeks confirmation from the server on whether it is permissible to proceed with the API call or if it should be rejected.
This is a way by which API’s can be more flexible rather than being restricted to one domain.
Now that we have essential concepts out of the way, let’s see how we can get around this error in the dev environment. Following are some ways to achieve this.
Establish a front-end proxy by utilizing a proxy server like Nginx. This proxy server functions as an intermediary positioned between the browser and both your web application and API server. As the browser executes your Single Page Application (SPA), the application exclusively references different paths within the same domain, where the base URL corresponds to the proxy server’s URL. In practice, the application consistently interacts with the proxy server first, which then directs the request to the relevant service based on the remaining part of the URL. This methodology deceives the browser into perceiving that all resources are accessed within the same domain, mitigating the need for CORS precautions.
Below is a sample configuration file for Nginx that facilitates the routing of requests to the appropriate services. Upon starting an Nginx server with this configuration, it seamlessly guides requests with the specified paths to their respective services.
location /api/ {
proxy_pass http://localhost:3200/;
}
location /api2/ {
proxy_pass http://localhost:3400/;
}
location / {
proxy_pass http://localhost:3000/;
}
}
Utilizing the default webpack dev server proxy is another option, which comes pre-packaged with the webpack server. Webpack is a popular build tool widely used for packaging modern front end applications.
A proxy setup can be achieved using a simple configuration. For example, when working with Vue JS, you can include the following lines in the vue.config.js file. This configuration empowers the webpack server to function as a proxy, capable of redirecting to multiple URLs.
Browsers can be started in a “security disabled” mode by issuing a command. For instance for Chrome, the command to be used is
google-chrome –disable-web-security –user-data-dir
Please Note: This option might be unsafe and should not be used, since starting the browser in a CORS disabled mode also means that the browser can open you up to potential vulnerabilities in other sites.
For more intricate scenarios, the configuration of CORS, including URLs and permitted methods, can be made dynamic. This involves storing such configuration data in a database, allowing for retrieval and enabling access accordingly. In the development environment, a more lenient setup might be suitable, while in production, specific URLs and methods could be defined to regulate access. The documentation for the cors middleware in Express.js provides an example of implementing such dynamic CORS configurations.
CORS is a complex topic and this blog just highlights the basic concepts behind CORS and the reason why it came into existence. This can hopefully serve as a starting point for you to explore further and to be able to handle this seemingly annoying error better.