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.