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.

techbad
  2009-07-22 17:43 Z