Monday, May 28, 2007

Cracking the Cross-Domain Security Problem

It's been a while since the last post, and since GSoC officially started today, it seems an appropriate time for an update. A lot has happened in the meantime. I graduated from college, interviewed in Austin and San Francisco, and reached something of a milestone on the g3p project. But that's enough about me, so let's get down to the details.

As I mentioned in a previous entry, attaching the AJAX application (compiled by the Google Web Toolkit) to the Google Gadget host module using an IFRAME was not my preferred method for doing things. The reason for this is because of a well-known security measure taken by most modern browsers that prevents scripts (i.e. Javascripts) in different windows (in the DOM sense of the word) but with different document.domain values from communicating with one another. There is a better explanation here.

Let me try to explain more clearly what the gadget is actually doing and why this is a problem. In the gadget's module definition, I create an IFRAME whose src attribute points to the GWT app's html file, which is located on my personal server. Since my domain is not the same as Google's, attaching the application in this way means I cannot pass data back and forth from the gadget module to the app contained in the IFRAME. However, in order to save preferences (or use any of the Gadget API's libraries), I need to be able to talk to the gadget module outside of the IFRAME.

Of course, Google is very aware of this problem, which is why they have different methods for including content in the gadget module (described here.) You might say that this seems like an application naturally fitted for the url content type. However, using type url would mean I have to have server-side code in order to get and set the gadget preferences, which I don't want. It also means I can't use their convenience methods for working with remote content.

So you looked at that link on working with remote content, and you are wondering why I can't just use those methods to retrieve the content from my server and latch it onto the gadget, while using a type html gadget. Well I'll tell you why, because this was actually my second choice strategy, and I spent some time implementing it this way before I eventually settled on my final solution. It is true that these methods would allow you to bypass the problem, since you could just create iframes and use document.write() to put the results returned by the content fetcher functions into these iframes. You would also need to do this for included scripts, however, which would just require adding an extra layer of "parent" to any existing code you have. No big deal, right? Well, sort of, but the GWT app itself uses iframes and their src attribute to include its components, and if you are going through all this trouble to use the GWT, why would you want to have to completely change the compiled output?

Alright, so it's not impossible to do it this way, but it's *REALLY* messy, so if this were my only option, I'd rather go for type url. But I don't want to do type url, and what I want to do should be possible and relatively painless. Afterall, the module definition is hosted on my server and Google proxies that content using an application running on their domain, so why shouldn't I be able to do the same for the rest of my content? Well, they do it for the module definition using a URL like this:

http://56.gmodules.com/ig/ifr?url=http://www.flatown.com/g3p/g3p.xml&nocache=2147483647&up_example=test&lang=en&country=us&.lang=en&.country=us&synd=ig&mid=56&parent=http://www.google.com

But that ifr app is only for parsing module XML files, so that doesn't really help. It does make you think, however, that they might be doing something similar in order to power those _IG_Fetch... functions. This was actually my first thought in the process, but I couldn't find the URL, and I figured Google would have let people know about it if it was something they wanted them doing.

So at this point, I was pretty stuck. No matter which direction I went, it looked like there was going to be a tradeoff between a serious rewrite of the GWT files (which someone else is supposed to be doing this summer anyway), and starting to write my own server-side stuff. I was about to bite the bullet and finish modifying the GWT to use _IG_Fetch, since I had just finished an experiment proving that this would be possible, when by serendipitous luck, a URL in the gadget message board caught my eye. It looked something like this:

http://gmodules.com/ig/proxy?

Sure enough, this was exactly what I was looking for: a proxy service on the gmodules domain to fetch any textual content. I reverted back to the old iframes, patched everything up to go through the proxy, and voila, I can now pass data to my app! Everything looks the same as before, but I've now included a patch script in the source code repository to be run every time the g3p is compiled. I also got rid of the history frame, but theoretically this could be made to work. If you want to know exactly what I need the script for, you can take a look at it here.

Anyway, I've really got to get going, even though I've left out some details and clarifications, they will have to wait for another time. Until then, have fun running circles around cross-domain security.

No comments: