Everyone who ever wanted to communicate in JavaScript between frames (or windows) from different domains knows, how painful that can be. And the same goes, if you'd like to communicate via XMLHTTPRequest from another server. There are hacks floating around to get around the limitations, but nothing is really satisfying.
But then again, I knew, that Opera had something implement to just allow this, but I never really could find more information about it. Until yesterday. It's called “ Cross-document messaging“, is actually in the Web Applications 1.0 Draft of whatwg.org and available in Opera since Version 8.0..
If you Google for it, you really don't find many pages which mention it, but there's one with an actual example. Starting from that, I wrote my own little cross domain thing using the LiveSearch feature from this blog. The result can be seen at trash.chregu.tv/xdom.html, which uses blog.bitflux.ch/inc/bx/php/livesearch.php for getting the results. As one can't directly call blog.bitflux.ch from trash.chregu.tv via XMLHTTPRequest, we have to open an iframe coming from blog.bitflux.ch ( blog.bitflux.ch/webinc/php/ls.html) and attach “message” event handlers to both documents (as we need a two way communication)
For the receiving frame (ls.html at blog.bitflux.ch) this looks like the following:
document.addEventListener('message',function(ev) {
parent = ev.source;
liveSearchDoSearch(ev.data)
},false);
Now, when we start typing at the main page (xdom.html at trash.chregu.tv), a keypress-eventhandle calls the above registered “message” event handler with:
var receiver = document.getElementsByTagName("iframe")[0].contentDocument;
receiver.postMessage(document.getElementById("livesearch").value);
ls.html then receives that event and calls liveSearchDoSearch, which does the XMLHTTPRequest to the blog.bitflux.ch domain. When the XHR call is finished, ls.html calls the “message” event on the other frame to tell, that we have a new result:
function liveSearchProcessReqChange() {
if (liveSearchReq.readyState == 4) {
parent.postMessage(liveSearchReq.responseText);
}
}
That event receiving code just looks like the following:
document.addEventListener("message",function(ev){
var p = document.getElementById('Status');
p.innerHTML = ev.data;
},false);
And that's it, easy cross-domain, cross-document messaging.
But what about security? If it's usually not allowed to do such things due to security concerns, why should that be suddenly ok? It isn't per se and if one uses that approach, you have to implement it “right”. First, the event object provided to the callback function has some more attributes than just “data”:
readonly attribute DOMString data;
readonly attribute DOMString domain;
readonly attribute DOMString uri;
readonly attribute Document source;
So, in the above example, if we want to be sure, that the callback only is called from the blog.bitflux.ch domain, we change that function to:
document.addEventListener("message",function(ev){
if (ev.url == 'blog.bitflux.ch') {
var p = document.getElementById('Status');
p.innerHTML = ev.data;
} else {
alert ("Intruder");
}
},false);
The main attack vector for cross-domain exploits usually is that you go to a foreign site and this site tries to contact a page from your intranet (or similar) and sends that information back to badsite.com. With good checks in the “message” event callback, you can easily prevent that. Of course, if you have an XSS exploit possibility somewhere on one of your “good” sites, then all this helps nothing, but then you've lost even without this feature :) And as always, never trust data coming from potentially untrusted sources (and the above is a pretty bad example, doing p.innerHTML directly from an untrusted source is not pretty :) )
A further reason making this quite secure is, that you don't get read-access to the whole document, but you can only push data to a document, which has an event listener for it. Therefore you can clearly define within that callback, what's allowed to do for an outside document and what not.
I didn't look very thoroughly into the security issues, but it looks “good enough” to me, as long as you implement it right. And all the hacks mentioned in the first paragraph for getting cross-domain support have basically the same “problem”.
Now let's just hope the other browser vendors also implement that feature, it would make the life of some web developers much easier, especially for mash-ups and alike. As it's in the whatwg.org draft, the chances are pretty good that at least Safari and Mozilla will implement it some day