Cross-Origin HTTP Requests

Cross-Origin Resource Sharing Overview

Cross-origin HTTP requests are usually blocked by the browser: cross-site scripting violates the same origin policy and exposes the user to latent security threats. However, sometimes it is desirable to allow a client script to access Web resources published under an extraneous domain: for example, CORS requests can be indispensable for third-party Web applications communicating with a cloud service. Developers should pay heed to both client and server parts of cross-origin message exchange: correct handling of HTTP requests is an essential component of application security.

A simple CORS scenario is based on the use of Origin and Access-Control-Allow-Origin HTTP headers. Let's assume a demo script is deployed at the fictitious http://example.com/js/cors-demo.html. The script is trying to retrieve a text resource published on another host. The client code employs an instance of XMLHttpRequest to obtain the text:

var xhr = new XMLHttpRequest();
xhr.onload = loadEvent => { // load event handler expressed as an arrow function
 console.log(loadEvent.target.responseText);
};
xhr.open('GET', 'http://example.net/cross-origin-request-handler.php');
xhr.responseType = 'text';
xhr.send();

The server-side code with PHP header() function is as minimalistic as necessary to yield an item of text data:

<?php
 header('Content-Type: text/plain; charset=UTF-8');
 echo 'This is just a demo';
?>

When the browser is about to send a cross-origin HTTP message, it appends the Origin to the list of request headers. In our demo the request origin is specified as

Origin: http://example.com

If the server response does not contain the Access-Control-Allow-Origin header with a value matching the Origin, the browser stops execution of JavaScript code and displays a console message informing the user that the Same Origin Policy disallows reading the remote resource.

In the PHP example above, the server does not define its reaction to CORS requests, so the load event of the XMLHttpRequest will never be fired.

Now let's change the server code by including a snippet with Access-Control-Allow-Origin. The value of the header can be specified as an exact domain (this is http://example.com in the demo) or as the * wildcard:

<?php
 header('Access-Control-Allow-Origin: *');
 header('Content-Type: text/plain; charset=UTF-8');
 echo 'This is just a demo';
?>

Here's another example in Python. For demonstration purposes, the Django code below permits any client script to get metainformation about request headers, WSGI properties and the server environment. A JS routine running from another origin parses the response payload as an instance of HTMLDocument and presents the retrieved data to the user:

Django view code (from 'views.py')
def corsDemo(request):
       meta = ['%s: %s' % (key, value) for key, value in sorted(request.META.items())]
       response = render_to_response('cors-demo.html', locals())
       response['Access-Control-Allow-Origin'] = '*'
       return response

Django template code (from 'cors-demo.html')
<div id='info'>
 {% for i in meta %}
  <div>{{i}}</div>
 {% endfor %}
</div>

CORS request generated by script running from another origin
var xhr = new XMLHttpRequest();
xhr.onload = evt => {
 var parser = new DOMParser();
 var info = parser.parseFromString(evt.target.responseText, 'text/html').getElementById('info');
 document.body.appendChild(info);
};
xhr.open('GET', 'http://example.info/cors-demo');
xhr.send();