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.

  • Daniel Silva

    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

  • http://www.zackgrossbart.com Zack Grossbart

    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

    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?

  • http://www.zackgrossbart.com Zack Grossbart

    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.

  • Pavan

    Hi Zack,

    Thank you for this helpful link. I used it to successfully run the cross-domain script with JQuery and JSONP on Mozilla Firefox (3.6.3) and IE 8. But the code errors out in IE 6 & 7.

    I want to use this approach in a live project. Is there a way to make it work in IE 6 & 7 as we would need to support from IE 6 onwards.

    the errors are as shown here:

    line 53,1 object ecpected at

    and line 19,9 expected identifier, string or number at the close of the $ajax({..});

    any workaround for the please?

    Thanks in advance,
    Pavan.

  • http://www.zackgrossbart.com Zack Grossbart

    Hello Pavan,

    I’m glad the code was useful for you. This should work in IE6 and IE7, but I haven’t tried it there. The only advice I can give is to debug and keep trying. Good luck.

    -Zack

  • Mike Haggerty

    Zack,

    Thanks for the helpful description and diagrams. I did want to point out an error, however. I’m fairly certain that the reason your code works is not because you have server1 returning a URL — it works b/c you are using the JSONP technique when making the request to server2. Try eliminating the step where you make a request to server1, and it should still work.

    JSONP overcomes the same origin policy by avoiding AJAX all together. Instead, what’s actually happening is that a new script tag is being created and added to the DOM. Of course, jQuery handles all of that for you, so it looks like a standard AJAX request. Since the script tag isn’t subject to the SOP, it can request resources from any domain. See http://www.west-wind.com/WebLog/posts/107136.aspx for more information.

    Hope that helps!

  • http://www.zackgrossbart.com Zack Grossbart

    Thanks for catching that Mike. I’ve noticed the same thing with JSONP. JQuery made it a little too easy for me the first time around and I got confused.

  • Mike Haggerty

    No problem!

    By the way, I’ve been poking around your site and must say that I’m a fan. It’s nice to find someone who is working in the enterprise who understands the crucial nature of good design.

    Also, I love the broswer-based presentation tool!

  • http://www.zackgrossbart.com Zack Grossbart

    Thank you so much. I’ve written more about design at GetTheEye. I wish I had the time to do even more.

  • aloleary

    The new CORS (Cross-Origin Resource Sharing) is very interesting when in comes to the SOP issue.

    https://developer.mozilla.org/En/HTTP_access_control

  • http://www.zackgrossbart.com Zack Grossbart

    I’ve been following that. The big question is, which browsers will support it.

  • Pingback: Same origin policy, SOP « Tony’s Blog

  • http://www.facebook.com/mpodskrobko Margarita Podskrobko

    “u00a0Now 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.”nnSorry, but this is very wrong.u00a0

  • http://www.zackgrossbart.com Zack Grossbart

    Thank you for the correction.u00a0 It turned out that was just way I wished it worked.u00a0 I guess it still could with some of the proposed specifications from Google.u00a0

  • D O

    You can overcome the same-origin policy usingu00a0http://anyorigin.com/ When you go there, just give it any url and it generates about 4 small lines of javascript/jquery code for you that lets you get the html/data, regardless of it’s origin. In other words, it makes any url or webpage a JSONP request. I’ve found it pretty useful :)

  • http://www.zackgrossbart.com Zack Grossbart

    Thanks for the tip. That works well for GET request. Have a good solution for PUT, POST, and DELETE?

  • lenin m

    very good tutorial.thank you! i use tomcat 7, added below in my web.xml, but tomcat not recognizing below url callback, I find JSON_CALLBACK() padding not happening, this means tomcat is not configured to support jsonp. please let me know how you configured since you used tomcat. thank you!

    http://10.162.181.174:8080/AngularSpringApp/resources/data/data2.json?callback=JSON_CALLBACK&f=json

    jsonpEnabled
    true