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+ |
Learn more
† Most browsers do not yet support multiple origin values. The specification is also a working draft, and subject to change.