Of-CORS It’s frustrating!

Mohammad Azhar Dec 5, 2023 Security

Introduction

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 or Cross Origin Resource Sharing error is an error that gets thrown as a result of the Same Origin Policy that was implemented by the browsers to protect users from XSRF (Cross Site Request Forgery) attacks.

What is CORS?

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.

How does CORS help?

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.

  • Different sub domains or domains for the web application and the API server

    An SPA, residing on a distinct domain like app.mysite.com, might require communication with API endpoints hosted on restapi.mysite.com or might access API’s from other third party servers.
  • APIs Exposed on a Platform

    When APIs are exposed as a platform for others to build applications, these applications, likely operating on different domains, need the freedom to interact with the provided APIs.
  • SaaS-Based Business with Separate URLs for each customer

    In a Software as a Service (SaaS) model where each business has a unique URL (e.g., ‘org1.mysite.com’ and ‘org2.mysite.com’), there could be instances where applications on these domains need to access a central service located at ‘api.mysite.com.’
  • Local Developer Environment

    In local development setups, where the front-end application runs on a distinct host:port combination compared to the back-end server, flexibility in cross-origin calls is crucial.

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.

How does CORS work?


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.

How to bypass CORS during development?

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.

Use a proxy server between front end and backend.

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.

rewrite_log onserver {
listen 3000;

location /api/ {
proxy_pass http://localhost:3200/;
}

location /api2/ {
proxy_pass http://localhost:3400/;
}

location / {
proxy_pass http://localhost:3000/;
}
}

Webpack proxy setting

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.

devServer: {
proxy: {
‘/api’: {
target: ‘http://localhost:3000’,
router: () => ‘http://localhost:3200’
}chrome can be started in a “security disabled” mode by issuing a command such as the following}
}

Disabling CORS while starting the browser

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.

Dynamic CORS configuration based on environment and data

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.

Conclusion

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.