Cross origin XMLHttpRequests with CORS

The communication of JavaScript applications with a backend system is in most cases based on XMLHttpRequests. This allows applications to load/save/… data without navigating to another page. As most developers know, this kind of communication has a big limitation caused by the so-called “same origin policy”.

Same origin policy

According to Wikipedia, the same origin policy is:

In computing, the same-origin policy is an important concept in the web application security model. Under the policy, a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. An origin is defined as a combination of URI scheme, hostname, and port number. This policy prevents a malicious script on one page from obtaining access to sensitive data on another web page through that page’s Document Object Model.

In short, the same origin policy is not a technical limitation but an artificial one implemented to protect the user. As this is a good for the overall security of the web, all major browsers implement the same origin policy.

Breaking the same origin policy with CORS

While the same origin policy is a good concept, there are legit cases when developers would like to implement so-called cross origin XMLHttpRequests to directly communicate with a server that is not the one where the HTML page came from.

This is why the CORS (Cross-origin resource sharing) specification was created. According to Wikipedia, CORS is:

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources (e.g. fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated.

A web page may freely embed images, style sheets, scripts, iframes, videos and some plugin content (such as Adobe Flash) from any other domain. However embedded web fonts and AJAX (XMLHttpRequest) requests have traditionally been limited to accessing the same domain as the parent web page (as per the same-origin security policy). “Cross-domain” AJAX requests are forbidden by default because of their ability to perform advanced requests (POST, PUT, DELETE and other types of HTTP requests, along with specifying custom HTTP headers) that introduce many security issues as described in cross-site scripting.

CORS defines a way in which a browser and server can interact to safely determine whether or not to allow the cross-origin request. It allows for more freedom and functionality than purely same-origin requests, but is more secure than simply allowing all cross-origin requests. It is a recommended standard of the W3C.

How does CORS work?

CORS uses special HTTP headers to define which cross-domain calls are explicitly allowed.

The client adds a request header “Origin” to specify the origin of the HTML page which tries to do the cross-origin call:

Origin: http://www.oio.de

The server adds a response header “Access-Control-Allow-Origin” to specify from which origins it accepts cross-origin calls:

Access-Control-Allow-Origin: http://www.oio.de

To specify that the server allows calls from all origins you can also use “*” as origin:

Access-Control-Allow-Origin: *

Preflight for more complex cases

The simple case shown above only applies for specific cases with some limitations:

  • The HTTP method must be GET, POST or HEAD
  • Only standard HTTP headers are allowed to be sent by the browser
  • For POST requests, only content types text/plain, application/x-www-form-urlencoded and multipart/form-data are allowed

In other cases, a so-called “preflighted request” is necessary. To do this, the browser will transparently send an additional request with method OPTIONS before sending the actual request. With the OPTIONS request, the browser and server exchange information about permissions regarding cross origin requests. In addition, the server defines how long the browser is allowed to cache this information to save additional OPTIONS request for further requests.

This special request contains the following information:

OPTIONS /some/path HTTP/1.1
Origin: Origin: http://www.oio.de
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-SPECIAL-HEADER

The matching response looks like this:

Access-Control-Allow-Origin: http://www.oio.de
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-SPECIAL-HEADER
Access-Control-Max-Age: 1728000

Cookies and authentication methods

To transmit cookies or support authentication (e.g. basic authentication), adjustments on both sides, client and server, are necessary.

On the client side, the field withCredentials of the XMLHttpRequest needs to be set to true to make the browser send cookies and handle authentication requirements from the server.

On the server side the header Access-Control-Allow-Credentials must be added to the response:

Access-Control-Allow-Credentials: true

Standardization and browser support

The CORS specification is in the state of a recommendation since 2014. Speaking of browser support, all major browsers provide implementations of CORS in their latest versions. As daily business isn’t so easy, it is a little bit more difficult:

Let’s have a look at the compatibility list at caniuse.com:

  • Current versions of Google Chrome/Firefox/Firefox ESR/Opera/IE have full support for CORS
  • To use CORS in IE8/9, you have to use a proprietary XDomainRequest instead of XMLHttpRequest which has some limitations
  • Current versions of Safari (Mac/iOS) and older but commonly used versions of Android have some limitations that do not affect XMLHttpRequest
  • Opera Mini doesn’t support CORS at all

So from a business view where Opera Mini isn’t very relevant: If you do not need to support IE versions before IE 10, you are totally safe. If you need to support IE 8/9 you need to adapt XDomainRequest on these browsers or you can use a polyfill for this.

Short URL for this post: http://wp.me/p4nxik-2r1
This entry was posted in Web as a Platform and tagged , , , . Bookmark the permalink.

Leave a Reply