JSONP and The Same Origin Policy

by Zack Grossbart on April 18, 2009

same_origin_small

AJAX connections from JavaScript are a little technology that is revolutionizing the Internet, but they have a severe limitation with the same origin policy. In Java, C++, C#, or any other fat client language I can call out to any server I want, but not in JavaScript.

Calling out to other servers makes it possible to do some very exciting things:

  1. Create flexible application architectures that separate the code you are writing onto different servers.
  2. Call REST-based APIs.
  3. Access code from third-party sistes like Twitter.

Same origin prevents this because of security concerns. If JavaScript could call other servers then a malicious JavaScript program could wait until you loaded a different site (maybe your bank) and then pass information back to a different server. These issues are tough to solve in the general case, but for specific applications they can be controlled. This article outlines a working example of one way around the same origin problem called JSONP.

JSONP is a subset of the JavaScript Object Notation (JSON). JSON lets a server send data back to JavaScript in a format that is very easy and fast for JavaScript to read.

How it works

Let’s take an in-depth look at how JSONP and this example works:

The client starts by making a request to the server. The server responds with some HTML that includes a little JavaScript. The JavaScript then uses AJAX to call back to the same server and request some JSON data. The server sends the JSON data.

Now the magic happens. Because the server with the same origin as the HTML sent the URL as part of the JSON data we can use that URL to connect to a second server. The URL is trusted by the JavaScript because it came from the original server. This is why JSONP works.

If you tried to make this call with standard JSON then you would get a security exception contacting the second server. JSONP takes your JSON request and pads it so you can call another server. This is where the name JSONP (JSON with padding) comes from.

The code for all of this is actually pretty simple. It is all in form.html.

function jsonptest() {
    output("Starting the JSONP test...");
    output("Making a request to jsonp.srv");
    $.ajax({
        dataType: 'jsonp',
        data: 'id=10',
        jsonp: 'jsonp_callback',
        url: 'jsonp.srv?dojsonp=true',1
        success: function () {
            alert("success...")
        },
    });
}

function jsonp123(data) {2
    output("Received a response from jsonp.srv");
    output("Calling to: " + data.url);
    $.ajax({
        dataType: 'jsonp',
        data: 'id=10',
        jsonp: 'jsonp_callback',
        url: data.url,3
        success: function () {
            alert("success...")
        },
    });
}

function done(data) {4
    output(data.message);
}

When you press the button it calls the jsonptest function. That function uses JQuery to make an AJAX call to the server1. The server returns some JSON data that automatically calls the function jsonp1232. That function takes the URL from the server and makes an AJAX call to the second server3. The second server processes the request and returns a JSON data structure that calls the done function4

jsonp_flow

The server code is even easier.

response.setContentType("application/json");
PrintWriter out = response.getWriter();

if (request.getRequestURL().indexOf(SECOND_SERVER_URL) > -1) {1
    String s = "done({\"message\" : \"You successfully bypassed the same origin " + 
                       "and made a request to " + request.getRequestURL() + "\"});";
    out.println(s);
    return;
}
String s = " jsonp123({\"url\" : \"" + SECOND_SERVER_URL + 
           "/jsonptest/jsonp.srv\"});";2
out.println(s);

If the request came to the second server URL our servlet will return the set of JSON data that calls the done function1. Otherwise it will call the jsonp123 function2. That is really all there is to it.

Try It out

Get the
source code

Now you’re ready to try the code for yourself. Start by downloading the source code for this sample. It can be found here. Once you have downloaded it you can unzip it to a work directory on your computer.

A small code change

This program needs to use well known URLs. I used http://localhost:8081 and http://foo.bit.com:8081. I don’t own the bit.com domain, I just changed my settings to make my computer think I did. I’ll talk more about this in the next section. You need to change this URL in the src/jsonptest/server/MainServlet.java file. The line you need to change looks like this:

private static final String SECOND_SERVER_URL = "http://foo.bit.com:8081";

Replace the http://foo.bit.com:8081 part with something that makes sense for you. The URL you use is totally up to you. You can use the hosts file. Microsoft has a help file about how to do it. In my case I used two different URLs, but the same server. My browser thinks http://localhost:8081 and http://foo.bit.com:8081 are two different servers, but they are really the same. This keeps the sample simple.

Build it

This program has been setup to be built using Apache Ant. Ant is a very popular tool for building Java projects. Building this project without Ant is possible, but it can be tricky to set up. If you don’t have Ant already you should download and install it.

Once you have installed Ant you can build and run the sample application by following these steps:

  1. Unzip the sample archive to a directory on your machine.
  2. Open a command prompt and change directories to the location you unzipped the sample to.
  3. Execute the command ant (you may need to provide the full path to your Ant installation).
  4. Copy the jsonptest.war file a servlet container. I used Tomcat, but Jetty, JBoss, or any of the other will work too.
  5. Run your server (for Tomcat that means running tomcat/bin/startup.bat or tomcat/bin/startup.sh
  6. Go to http://localhost:8080/jsonptest/form.html. Your port might be different depending on your configuration.

The Catch

This code does successfully work around the same origin restriction. However, there are a few issues you should consider before using it.

Terrible error handling

There is basically no good way to handle errors contacting the second server. If that server sends an HTTP error code you just won’t see it.

Big security hole

Same origin is a good policy most of the time and this code works around it. That is a giant security hole. The server one is vouching for server two. If server one is compromised in any way (like a script injection attack) it will make any server on the Internet trusted.

Run it everywhere

This sample is a JavaScript client application and a Java servlet server. This client should run on all of the major browsers and the server should run on Windows, Linux, MacOS, and any other platform where Java is available.

The code in this application is free and is released under the Apache 2.0 license. That means you are welcome to use, copy, and change this code as much as you would like. If you find any bugs, have any comments, or make any improvements I would love to hear from you. Other programs are needed to run this application. They are all free, but some of them use different licenses. You should make sure to read and understand each license before using a product.

Conclusion

The same origin policy is a good one most of the time, but as web sites become web applications they need the added flexibility and power of calling other servers. JSONP is a little complicated, but it works. It can help your application, but this is definitely varsity level code. Never use JSONP without a good plan managing security concerns and some very defensive coding.

See a problem with this idea? A way to make the code better? A great application you can write with JSONP? Leave a comment and let us know.

Part of the image in this document was made using an image by Dr. E.F. Tymac. That image and the resulting image are both licensed under the Creative Commons Attribution-ShareAlike 2.5 License.