
For my recent
geolocation demo I wanted a web page hosted on
somebits.com to be all Web 2.0 and dynamically load data from
geonames.org. I’ve never really done AJAX before so I was surprised I couldn’t do this remote call because of security restrictions. And I was even more horrified at the workaround. I couldn’t find any place where all this is succinctly explained, so here’s some notes.
The same-origin policy is a rule web browsers enforce for security. Basically, Javascript code running on a webpage at somebits.com cannot access any resources from any other domains. No access to the DOM of other sites, no cookies, no frames. And, important for AJAX, no XMLHttpRequest to a remote domain. So much for mashups where you load data from multiple sites!
There’s a variety
of workarounds. The one I used is JSONP
which turns the declarative JSON
data you would load from a web service into imperative Javascript
code. Where a JSON reply from a web service might be
{"balance": "3942.12"}
A JSONP reply would include a function call:
callback({"balance" : "3942.12"})
Why wrap data in a
function call? Because the same-origin policy
does not apply to
scripts! A browser won’t let you load a few bytes from a remote
server as data but it will happily load those same few bytes as code
and execute it. And so you execute the remote JSONP in your page and
it calls your
callback() function to use the data. A kludge, but frameworks like
jQuery hide the mess.
At first blush this seems crazy. It’s insecure to let me load remote
data but it’s fine to let me run remote code? But I’d misunderstood
the purpose of the same origin policy. The reason
somebits.com can’t load stuff from geonames.org
isn’t to protect somebits, it’s to protect geonames!
Without this policy XSS
attacks would be trivial; any Javascript code in your browser
could steal cookies from other sites, inspect private data, etc. Apparently executable Javascript is considered not-private, so I’m allowed to execute anyone else’s code anywhere. (Terrible assumption, no doubt there’s already a class of XSS attacks against JSONP services.)
Frankly the whole thing
smells
rotten and shows just how complex and twisted the Javascript
security model is. There’s a couple of proposals for more cleanly enabling remote web service requests. Firefox 3.5 implements HTTP access control, a W3C standard where a website can say “go ahead and let anyone load this content remotely”. IE8 implements XDomainRequest, which is restricted to “anonymous” requests to limit security exposure. And so the squabble continues.