Look Ma, Cross-Domain Scripting!

Ajax, as I’ve stated time and time again, is sweet. So what is problem? XMLHTTPRequest requires that the called scripts that execute server side and return information to the client must reside on the same domain. This has irked me time and time again during my exploration and experimentation of the Web 2.0 world.

Enter JSON Web Services and dynamic script tags. This beauty allows for Ajax-like communication across domains without requiring page loads. Right right right…so how do you use it? Read on…

First, lets clarify what JSON (JavaScript Object Notation) is. Wikipedia explains that JSON is just a way to structure object data in a way that is easily interpreted…primarily with JavaScript. Here’s some examples from Wikipedia:

JSON Example:

bork({"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}});

Which is the same as this XML Example:

<menu id="file" value="File">
  <popup>
    <menuitem value="New" onclick="CreateNewDoc()" />
    <menuitem value="Open" onclick="OpenDoc()" />
    <menuitem value="Close" onclick="CloseDoc()" />
  </popup>
</menu>

So…if JSON just the data representation part of things, how does it work similarly to Ajax?

Now we’re getting into the Web Services side of things. The quick answer: When you dynamically create <script> tags on a page, the JavaScript source is executed immediately. Now, as most experimental web developers may well know, you can reference remote JavaScript source in a <script> tag. Which means….if you dynamically create a script tag that references a source on another domain, and that source outputs JSON, you are as good as gold!

Here’s the step by step: (I use a JavaScript Object I found over at Dan Theurer’s Blog for the dynamic adding of script elements…it helps avoid duplicate script tags).

First, lets make the remote file that outputs JSON. (obviously, most cases will have the data dynamically generated…but I’ll use a static example as the generation of this data is trivial)

file: remote_file.php

< ?php
echo 'bork({"Image": { "Width":500, "Height":250, "Title":"Giant Cow", "Thumbnail":{"Url":"http://someurl.com/image/1234", "Height": 75, "Width": 150}}});';
?>

Here’s the html file we want the user to see/interact with:

file: client.html

<html>
<head><title>Test</title></head>
<script src="js/jsr_class.js" type="text/javascript"></script>
<script type="text/javascript">
function addScript()
{
  var obj=new JSONscriptRequest('http://someurl.com/remote_file.php');      
  obj.buildScriptTag(); // Build the script tag      
  obj.addScriptTag(); // Execute (add) the script tag
}//end addScript

function bork(data)
{
  var text='';

  if(data==null)
    alert('error');
  else
  {
    text='Image Title: ' + data.Image.Title + '<br />';
    text+='Width: ' + data.Image.Width + '<br />';
    text+='Height: ' + data.Image.Height + '<br />';
    text+='Thumbnail Data: ' + data.Image.Thumbnail.Url;
    text+=' (' + data.Image.Thumbnail.Width + 'x' + data.Image.Thumbnail.Height + ')<br />';
  }
  document.getElementById('output').innerHTML=text;
}//end bork
</script>
<body>
<a href="#" onClick="addScript(); return false;">Click to Get Stuff</a>
<div id="output"></div>
</body>
</html>

Thats all there is to it! Simple and slick.


Comments

31 responses to “Look Ma, Cross-Domain Scripting!”

  1. Good example of scripting around browser securities. Should see greater use of this in the future. It still limited by browser support though.

  2. Bill Wilson Avatar
    Bill Wilson

    I’m looking at your code and it doesn’t explain exactly how this works, it just provides us with code to use. I’m curious as to what makes JSON work and what differences JSON has from XML. Is there any draw-backs or negative effects of using your solution? Is it a hack that will be fixed in the future?

  3. […] I have often mentioned my process of expanding my proficiency of Ajax. Through my journey I have made a number of wrong turns and hit my share of stumbling blocks. All of that has been a learning experience and I’m learning still. I began fiddling with XMLHttpRequest as many do – blissfully ignorant of the many frameworks that exist to make Ajax super easy. My code was bloated with some neat…’features’ (pronounced: bugs). […]

  4. […] As we’ve begun adopting Ajax, JSON, and similar JavaScript heavy technologies a problem quickly arose. Suddenly our clean HTML was being cluttered with tons of script tags, onclicks, and other various event handling functions. Trying to extract this logic back out of the HTML was a definite desire for us. […]

  5. […] MasterWish was built using SAJAX as the tool of choice for Ajax communication but as I’ve mentioned in the past, I am a Prototype convert. My knowledge of Ajax, JSON, and general application structure has been morphing so much in recent weeks that I have held off in completely revamping the wish list site. […]

  6. […] After my post titled Look Ma, Cross Domain Scripting! a while back, I received a comment that was seeking more information. The commenter posts: I’m looking at your code and it doesn’t explain exactly how this works, it just provides us with code to use. I’m curious as to what makes JSON work and what differences JSON has from XML. Is there any draw-backs or negative effects of using your solution? Is it a hack that will be fixed in the future? […]

  7. crap man….at least put up a working sample so we can view. I have no idea how this really works.

  8. I’ve updated it and it seems to work for me. Here is another example that I lifted from Yahoo.

  9. Your example did not work for me. The other example from yahoo only works for me in firefox, IE returns nothing. Is JSON not cross browser ?

  10. wow – works great although i did have to change the sample page to get it to work

    note you must have a parameter os some type like ?a=b because the JSON script adds a timestamp to the end to prevent caching ….. “‘&noCacheIE=’ + (new Date()).getTime();”

    var obj=new JSONscriptRequest(‘http://www.netlert.com/includes/crossdomaintest2.php?a=b’);

    however don’t understand this part
    “….if you dynamically create a script tag that references a source on another domain, and that source outputs JSON, you are as good as gold!”

    i just created a simple php page like so
    bbbbb”);’;
    ?>

    and added a function to my page

    function test(a, b){
    var e = document.getElementById(‘output’);
    e.innerHTML = a + ” ” + (new Date()).getTime();
    e.innerHTML += b;
    }

    so I don’t see why you have to use JSON notation – anyway, i tested this on the major browsers – netscape 6.2 and up – firefox – opera, ect and it worked fine.

    However I created a loop at the bottom and there is a slow memory leak :(

    function loop(){
    addScript();
    setTimeout(“loop();”, 1000);
    }
    setTimeout(“loop();”, 1000);

    actually i take that back – I made the obj var global and in my test script I do a obj.removeScriptTag(); and it seems to fix the memory leak.

    anyway thats my input – this is awesome!!!

  11. wow – this is great – you have to modify the test to get it to work

    you have to add a parameter on the end of the JSON object like so
    obj=new JSONscriptRequest(‘http://www.netlert.com/includes/crossdomaintest2.php?a=b’); because the script adds a timestamp to prevent caching ‘&noCacheIE=’ + (new Date()).getTime();

    The other thing is if you are making a lot of calls over time there is a memory leak so you have to make the JSON object global so you can call obj.removeScriptTag();

    var obj;
    function addScript()
    {
    obj=new JSONscriptRequest(‘http://www.netlert.com/includes/crossdomaintest2.php?a=b’);
    obj.buildScriptTag(); // Build the script tag
    obj.addScriptTag(); // Execute (add) the script tag
    }

    however i don’t understand this part “if you dynamically create a script tag that references a source on another domain, and that source outputs JSON, you are as good as gold!”

    I just created a simple php page like so
    bbbbb”);’;
    ?>

    and added my own function
    function test(a, b){
    var e = document.getElementById(‘output’);
    e.innerHTML = a + ” ” + (new Date()).getTime();
    e.innerHTML += b;
    obj.removeScriptTag();
    }

    works great – this is awesome thanks!

  12. I’ve got a question about security.
    How can I, when U include a PHP page that generates JSON, restrict it to just one website?
    So no one can include the page, just by viewing the source and copy the uri.
    I tried it to check whether the HTTP_REFERER is the same as a var set in the PHP file.
    But i found that the HTTP_REFERER is not always set, you can disable it in your browser settings..

    anybody got an sollution?

    btw: nice script!

  13. Basically what you need to do is use a ‘secret key’ only known to your application (similar to what Flickr does with their API key) and output the appropriate JSON if the the key is legit. The easiest way is to first create a string you want as your key…like:

    dkdlk9090873dnv986c6980u3jf9

    Then when you are do your actual inclusion of the script, pass along a timestamp and an md5 hash of the ‘timestamp concatenated with your key’. On the PHP side of things, your script already knows the key and the passed timestamp can be concatenated with the key and md5’d then checked against the passed in md5 hash. So basically, you’ll have something like this:

    <script type=”text/javascript” src=”http://something.com/some/script.php?timestamp=123456789&hash=bf8db5cb4ec88ee6f0d537a6594fe9cc”></script>

    (the hash is an md5 of “123456789dkdlk9090873dnv986c6980u3jf9”, timestamp and key)

    On the PHP side of the world, you have the following:


    <?php
    $key='dkdlk9090873dnv986c6980u3jf9';
    $min_timestamp=strtotime('3 hours ago');
    $max_timestamp=strtotime('3 hours from now');

    //make sure the passed hash is legit
    if($_GET['hash']==md5($_GET['timestamp'].$key))
    {
      //make sure the timestamp falls within a reasonable amount of time
      if($_GET['timestamp'] >= $min_timestamp && $_GET['timestamp']< =$max_timestamp)   {     //output your data here   }//end if }//end if ?>

    Of course, you can adjust the time window to your application’s needs as appropriate. If you need a client-side md5 library in JS…they exist out there. (i.e. I don’t know where one is at the top of my hat)

  14. I understand what you are saying, the only problem i have now is: how can i add more sites, every site is a single user and the application is only ristricted to their site. Now the problem with your code is that everyone could just copy and paste it in their website and use the script intended for someone else. The script is like a external login, you login in your website and then a admin page is loaded from another server where the user can manage some hosting/website updates for his site/hosting.

  15. Now the problem with your code is that everyone could just copy and paste it in their website and use the script intended for someone else.

    So that’s the point of the workaround, right (to take advantage of legal operations in a browser to allow you to load scripts from other sites dynamically)? If I understand you correctly, and you’re trying to restrict people from linking to your scripts from offsite (which is kinda not really on tt) in any surefire fashion; what you are looking for is something like a perl handler on mod_perl that serves the bodies of scripts for you. Then before the handler decides if it will write your script to the browser or write “Screw you hax0r”, you have access to the whole host of apache’s information — you can then always see the full URI for the script and filter accordingly.

    Oh also — kudos on this script too. =)

  16. Hi, is there any way to send post variables to the php (server side) script?

  17. In addition poker heads up online buy coffeehouse ring joint line four same day cash advance loan street seven corner deuces amount fold download giochi juice deck game round fill!

  18. […] JSON. ….. 1 – javascript pure cross-domain scripting 1 – ajax jquery json cross domains …http://borkweb.com/story/look-ma-cross-domain-scriptingSedna RSS jQuery.infojson parser doesn’t output – pedalpete Discuss jquery mailing-list … DWR for […]

  19. I cannot seem to get your example working. Everything is error-free, but my callback function (bork) never gets visited. I’ve copy/pasted your client code exactly, changed the remote URL to this: http://www.twonkyvision.com/test.php (no that’s not a valid software license format), and made the first line in bork() to just send out a simple alert box. But my alert never gets visited, but no javascript errors showing up in my console.

    Am I missing something? My client box is MS IIS, but my server box is LAMP, would that be a problem?

    Thanks!!

  20. […] B was running from host A and doing an XHTTPRequest request to host B. So what to do? Well I found this article on using JSON to do cross domain scripting (and here is even more background) but it […]

  21. The problem I have with a technique like this is the amount of hoops you have to jump through to control who can and cannot get to content cross-domain.

    Enter: flXHR (pronounced flex-er) http://flxhr.flensed.com/

    It’s a javascript+flash combination that uses an invisible swf instance to act as a proxy and leverage Adobe’s server-opt-in model for crossdomain.xml policies to allow authorized cross-domain communication.

    flXHR implements an identical API to the normal native XHR, which makes it a super-easy drop-in replacement, meaning it can effortlessly be used with any number of various JS frameworks that already know how to speak XHR API.

    It’s client-side cross-domain, without all the hassle, and with much more reliability.

  22. Thanks – great article – just getting into using json with google maps. My question? Why is the funtion bork never used in this example – to my seeing at least? Am I missing something? Thanks much…

  23. It’s working for me now.

    As cornwell said, you have to put something like ‘&a=b’ behind the URL. Without that it does not work.

    The function ‘bork’ is called by the remote_file.php itself after it has been included in the head of the code.

  24. Guys,
    I think most of you are not realising that this has nothing to do with json.
    All you need to do is call a server file and return a javascript like file.
    So if you want xml all you need to do is return

    var xmltext=””

  25. Hi,

    I tried this code exactly as it is mentioned in the above example (using yahoo url), but it is just outputting the URL that I am providing in remote_file.php. Not returning the actual html source.

    What should I do?

  26. This is a great script, but what I have a question. We are trying to do is have users access our site http://www.htotelspy.com but be logged in even though they are coming to us from a link on our site http://www.superads.com. If we use this JSON solution is the hotelspy.com content going to be indexable in the search engines?

  27. That was supposed to be our site http://www.hotelspy.com by the way…sorry for the typo.

  28. […] @ borkweb offers this javascript class example:here Categories: Code, php Tags: cross domain, php, programming, script Comments (0) […]

  29. cornwell is right in what he’s saying, but it’s hard to understand ;-). To get this workin’ simple try to add the parameter “?a=b” to the JSONscriptRequest-URL.