Tiffany B. Brown

10 October 2011

HTML5 for AS3 Developers: cross-domain.xml and Cross-Origin Resource Sharing

This is the second post in an occasional series designed to bridge the gap between ActionScript 3.0 and emerging front-end technologies.

Flash, like JavaScript, more-or-less adheres to a same-origin policy by default. Under a same-origin policy, requests for data must come from the same scheme, hostname, and port. If http://foo.example tries to request data from http://bar.example, the request will usually fail.

Same-origin policies are designed to prevent the unauthorized leakage of data to a third-party server. Without it, a script or SWF hosted on http://mightbeevil.foo could read data hosted on http://goodsite.foo and send it to http://muhahahaevilsite.foo. This kind of cross-domain activity could be used to exploit cookie and authentication data. It's clearly a bad thing.

Recent browsers have safeguarded against these kinds of cross-site scripting exploits by preventing JavaScript from making cross-origin requests. XMLHttpRequest, for example, will throw a security exception if you attempt a cross-origin request.

Flash, meanwhile has long supported a means for enabling cross-origin requests: the policy file. The policy file is a way of white-listing requests for data or credentials from specific origins. It lives on the server from which you are requesting data, and gives the Flash player a "yay" or "nay" when asked whether the request from a specific origin should be allowed to complete.

Cross-origin restrictions, though necessary, are also quite limiting. You can't (or couldn't), for example, request data for a mash-up using XMLHttpRequest. Though there are workarounds — using dynamic script insertion, or using the document.domain — those workarounds also leave the DOM vulnerable to cross-site scripting.

To to mitigate the dangers of cross-site scripting while still enabling it, the W3C is developing the Cross-Origin Resource Sharing (CORS) specification. It functions similarly to Flash’s cross-domain policy file, but uses HTTP headers instead of an XML configuration file.

CORS request headers are automatically generated by conforming browsers when a script attempts a cross-domain request. Response headers must be set in the server's configuration file, or dynamically per URL using a server-side language.

Let’s compare a sample cross-domain.xml file to how we'd achieve the same thing using CORS.

Cross-origin requests from Flash

To use the domains from our example above, if http://mightbeevil.foo made a request to data hosted on http://goodsite.foo, http://goodsite.foo would need to permit the request by including mightbeevil.foo it in its policy file. For example:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="mightbeevil.foo"/>
</cross-domain-policy>

This file must be stored in the web root of http://goodsite.foo. It explicitly permits mightbeevil.foo — and permits only mightbeevil.foo — to make requests for its data (from within a Flash movie).

Cross-origin requests from the DOM

To reuse our example from above, let’s use XMLHttpRequest to make a request from http://mightbeevil.foo to http://goodsite.foo.

var xhr, onLoadHandler

onLoadHandler = function(event){
     alert('It is done!');
}

xhr = new XMLHttpRequest();
xhr.open('GET','http://goodsite.foo/data.json');
xhr.onload = onLoadHandler;
xhr.send(null);

It looks just like a regular XHR request, except for the fact that we're requesting data from another origin. Let's take a look at our headers.

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding:gzip, deflate
Accept-Language:en-us,en;q=0.5
Connection:keep-alive
Host:goodsite.foo
Origin:http://mightbeevil.foo
Referer:http://mightbeevil.foo/make_cross_domain_request/
User-Agent: Awesome/9.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0) FantasticEngine/8889876 Awesome Browser/9.0

Notice that our headers include Origin:http://mightbeevil.foo.

Goodsite.foo responds with the following headers.

Access-Control-Allow-Origin:http://mightbeevil.foo
Connection:Keep-Alive
Content-Length:1349
Content-Type:application/json
Date:Mon, 26 Sep 2011 04:44:50 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.2.20

Here we see that an Access-Control-Allow-Origin response header is returned by the server. Like allow-access-from, it indicates which domain(s) are allowed to make requests. Here, we want to know whether mightbeevil.foo is allowed to request data. It is, so the request will be completed.

Acceptable values for Access-Control-Allow-Origin include an origin (scheme + host + port), a comma-separated list of origins†, or a wildcard (*). As with cross-domain.xml, if the value of Access-Control-Allow-Origin had instead been http://notevil.foo, the request would have failed. Using a wildcard allows requests from any domain.

Of course, both specifications are more complex than what I have covered here. These examples illustrate how to enable a basic cross-origin request. It is also possible with both CORS and Flash to permit or exclude custom headers. And in the case of CORS, it is possible to use methods such as PUT or DELETE if the user agent supports it.

Opera Opera Mini Opera Mobile IE Firefox Chrome Safari iOS Safari Android WebKit


11.60+ no no 8.0+ 4.0+ 5.0+ 4.0+ 3.2+ 2.1+

Browser support for Cross-Origin Resource Sharing as of 10 January 2012

Learn more

† Most browsers do not yet support multiple origin values. The specification is also a working draft, and subject to change.