Introduction to Cross-Domain Ajax

From Jimbojw.com

Jump to: navigation, search
Tip: This is part of the Weekly Web Hack article series. If you like it, please subscribe to my blog. Thanks!

Update If you like this, continue reading Cross-domain Ajax via Flash


This week's Weekly Web Hack is the first in a four part series on cross-domain scripting. Here I'll review the nature of the same-domain policy, and how this hinders next-generation web app development. I'll also go over existing "solutions" to set the stage for the upcoming articles.

What's this all about?

First, lets talk about the problem for a bit. It goes by the name same origin policy, and this explanation from mozilla.org is typical:

The same origin policy prevents document or script loaded from one origin from getting or setting properties of a document from a different origin.
-- From http://www.mozilla.org/projects/security/components/same-origin.html

This means that it is largely impossible to load scripts from one domain into another. For example, the following are prohibited:

  • Issuing an XMLHttpRequest() to another domain (a core component of Ajax).
  • Accessing or modifying the DOM of a <frame> or <iframe> which has a src attribute with another domain.
  • Accessing or modifying another window (or tab) which has a different location.

So what's the problem?

The same-origin policy exists to prevent malicious use of resources. If there were no rules governing cross-domain script access, it would be trivial to wreak all manner of havoc on unsuspecting users. It would be easy, for example, for a malicious website to grab your session information to another site and execute actions on your behalf.

For one example, consider this:

  • You go to your favorite webmail program - it could be Gmail, Yahoo mail, Hotmail, or a private internal company webmail program.
  • After signing in and checking your email, you click a link to a malicious site which opens in a new tab.
  • The malicious site checks the http referer and sees that you came from your email account.
  • Using cross-domain scripting, the malicious site reaches back across into your email tab and downloads your address book and all your emails (or however many it can get before you close the popup).
  • Subsequently, after scanning your emails for passwords, financial data and other sensitive materials, it sends all your contacts an email from you endorsing the same site.

And that's just one example. A more insidious plot would involve a malicious third party using your browser to spider your company's intranet, leaking classified information with you as the unwitting accomplice!

Then the same origin policy is a Good Thing, right?

Well, yes and no. The same origin policy is good at keeping scenarios like the aforementioned from occurring, but it's not the only way, and it has some significant drawbacks. For example, consider Yahoo!'s variety of web services. The Yahoo! Developer Network site openly advocates using their services in building web applications. However, without the proper setup, an unknowing web developer is destined to run face first into the same-origin policy.

Clearly the developer's server isn't in the yahooapis.com subdomain where data will be queried from, so how can Ajax requests be issued to their web servers?

Proxy, proxy, proxy

To Yahoo!'s credit, they're at-the-ready to explain how to use a web proxy for cross-domain XMLHttpRequest calls. Incidentally, this procedure of using your own web server as an intermediary to third-party web services is often the first one new developers will find, and it's also potentially the most costly.

As Joe Walker from Getahead explains:

Why Do People Want Cross-Domain XHR?
Because it makes SOA happen in a way that it can't now. Currently if I think of a great mash-up, I will need a fancy server to make it happen, I have a lot of unnecessary extra coding, and when I get Digged or Slashdotted, I'm dead. With Cross-Domain XHR, all I need is Ajax and I'm all set. No server side computation required, no server side coding, no needing to make calls out of your server, life is a lot simpler and you have a much greater chance of surviving becoming famous.
-- From http://getahead.org/dwr/ajax/cross-domain-xhr

Due to the server requirement, this is not always a desirable option. Plus, if you're going to invest in the requisite infrastructure to set up a scalable, performant web-proxy, then you'll probably end up implementing some server-side caching to cut down on total bandwidth. And at that point, you're really providing enough of a service that adding your own value-added features (data aggregation for example) makes sense. Scope creep anyone?

Another problem is the same-IP funnel. If the remote site isn't actively supporting third-party development against their public APIs, they may simply block your server's IP from incoming requests when it becomes a noticeable fraction of their total traffic. Changing your server's IP address may be costly, difficult, or even impossible in some circumstances.

What else is there?

About the only other way to do cross-site scripting effectively is by way of On-Demand Javascript. In this very simple technique, you inject new <script> elements into your web-app's DOM, with dynamically created src attributes. This behaves much like an XMLHttpRequest() using the GET method, but without any domain restrictions.

The one caveat however is that the third-party's service has to output valid JavaScript. Ideally, that third party supports some kind of JSONP reply structure, but sometimes you can get by with just JSON IF the object is wrapped in an Array. By overloading the Array class constructor you can actually "execute" the data, but this is rather hacky and easily defeated.

Gmail was susceptible to this kind of cross-domain scripting, but closed the hole over a year ago, shortly after it was discovered. Jeremiah Grossman has an excellent writeup of these advanced web attack techniques using Gmail.

In any case, to use On-Demand Javascript, the third party has to provide valid JavaScript as the Ajax response. There are notable instances of this in the wild, for example Google Suggest returns JavaScript in its Ajax responses, which it executes via an eval() call*. However, this is in the vast minority of cases.

* Please see my previous web hack What Would Google Suggest? for more details.

Conclusions

Clearly the state of cross-domain scripting is not to the advantage of the aspiring web developer. Although the cross-domain policy in place today keeps bad things from happening, it is much too restrictive to promote (or even allow) the next-generation of web-applications to be developed.

Over the course of the next few weekly web hacks, I'll go over some lesser known and never-before-seen solutions to enabling cross-domain Ajax. Stay tuned!

Hope this helps. As always, I'll be happy to answer any questions.

Comments

Lloyd Dalton said ...

Hi,

There's also a flash-based approach: http://www.grouplite.com/f4a/

- Lloyd

--Lloyd Dalton 13:52, 20 December 2007 (MST)

Anonymous said ...

This site has a very detailed explanation of the same origin policy and associated risks: http://taossa.com/index.php/2007/02/08/same-origin-policy/ You might want to mention it as a more thorough reference.


--Anonymous 15:10, 21 December 2007 (MST)

Jimbojw said ...

Hi Lloyd,

That's a very interesting piece of software. In my research for this series, I found several mentions of a Flash based solution, and even some (bad) implementations. Unfortunately I hadn't come across that one in particular.

Thanks for the tip! I'll definitely give that one a shot.

--Jimbojw 16:15, 21 December 2007 (MST)

Jimbojw said ...

Hi Anonymous,

While researching for this article mini-series, I did come across that article. You're absolutely right that it's a very thorough resource describing the same origin policy and attacks against it.

Thanks for commenting!

--Jimbojw 16:17, 21 December 2007 (MST)

vaibhav said ...

can anybody give me code for cross domains access of ajax script???? thanx in advance

--vaibhav 22:28, 11 February 2008 (MST)

Matt said ...

I'm trying to call this API which returns a JSON object... http://developer.whitepages.com/docs/JSON

But I can't due to the restrictions of cross-domain calls mentioned above. I'm new to using JSON (and cross-domain calls) and was wondering if you have any ideas how I can call the whitepages API from a website on a different domain. Am I missing something easy? Or can it just not be done for security reasons? Thanks!

--Matt 15:07, 10 April 2008 (MST)

Jimbojw said ...

Hi Matt,

Well, there is a way to pull Json data in a cross-domain fashion, but it only works in Firefox 2.0, and it requires a bit of very clever JavaScript (overloading the Array constructor temporarily, then reverting it).

If you want to do cross-domain Ajax, you'll want to use a technique referred to as On-Demand JavaScript in which the data service will return not just the JSON data, but a little chunk of wrapper code right in front.

So for example, instead of returning:

{"key":"value","foo":"bar"}

It would return something like this:

callback({"key":"value","foo":"bar"});

Then, the caller script would assign a new function to callback like so:

window.callback = function( obj ) {
  alert( "key = " + obj.key ); // Alerts "value"
  alert( "foo = " + obj.foo ); // Alerts "bar"
}

Prefixing the JSON in that fashion has been refered to as JSONP, and can be done by having the web service take an additional "callback" parameter to define what should be placed in front.

This is how Yahoo APIs work in fact, they take a URL param called _callback for JavaScript based web services. Yahoo Pipes is one such example service.

--Jimbojw 14:58, 11 April 2008 (MST)

spb@doctorunix.com said ...

What about this little gem:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<!-- Policy file for http://www.doubleclick.net -->
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>

doesn't that just make it all wide open? Are there security risks associated with this?

I actually found this code on americanexpress.com

--spb@doctorunix.com 10:16, 21 April 2008 (MST)

Girish Singh said ...

You do not strictly have to use JSON for cross domain Javascript. As a rule of thumb you cannot directly access the javascript from one domain to the other. However you can pass messages and data across which can then accordingly trigger events in the javascript.

One way is to the use a proxy in between the two domains and relay an AJAX request to the other domain through the proxy. A detailed article on it is on http://www.mabaloo.com/Web-Development/Pear-HTTP-Request-A-Cross-Domain-AJAX-focused-tutorial.html

--Girish Singh 12:20, 3 May 2008 (MST)

Girish Singh said ...

Another way which does not involve a proxy but uses Iframes is by using the URL hash. http://www.mabaloo.com/Web-Development/Cross-Domain-Message-Passing-using-Iframe.html

--Girish Singh 12:20, 3 May 2008 (MST)


Got something to say?

Leave a comment
Sorry, comments are disabled.