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.

jsonp_flow

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

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.

{ 4 comments… read them below or add one }

Daniel Silva January 21, 2010 at 1:23 pm

I’m developing a twitter app in JS and I’m getting a same origin policy problem when trying to authenticate, it used to happen with all requests but after changing the datatype from json to jsonp the problem was genneraly solved across the application, except on the authentication process (it’s not json but html). What I find weird is that all the requests are made to the same server, so I don’t really get what is the issue here. The twitter uses a REST API, but once more I don’t really get what’s the specific problem of calling on a REST API in JS.

If you could enlighten me I would be very thankful,

Daniel Silva

Zack Grossbart January 21, 2010 at 2:11 pm

Hello Daniel,

It is difficult for me to debug your issue since I’m not working on your code, but I can give you a few guesses. If the authentication process is using standard HTML then it doesn’t have anything to do with your JSONP.

The real issue with REST and JSONP is that JSONP only supports GET and REST requires POST, PUT, and DELETE at a minimum.

Daniel Silva January 21, 2010 at 4:39 pm

But Can you explain me why REST-based APIs are affected by Same Origin Policy?
I just make a request to one server, or not?

Zack Grossbart January 21, 2010 at 4:51 pm

All REST requests are just HTTP requests. When you make an HTTP request from a browser it is an AJAX request. AJAX request are governed by the same origin policy which prevents you from calling to a different web server from inside JavaScript.

JSONP is a limited type of AJAX request that can get around the same origin policy, but it doesn’t support the full implementation of HTTP that REST needs. If you are calling REST from JavaScript you are limited to calling the server that served your JavaScript.

Leave a Comment

You can edit your comment after you submit it.