Ajax, CORS, JSONP and the battle with Same-Origin Policy

Posted by Ghassan Karwchan on Wed, Dec 16, 2015

All modern browsers have a built-in security policy called Same-origin policy, which help mitigate many vulnerabilities and security flaws. This policy means the browser can only pull data from the same site.
Same site means pages that share the scheme (http, ftp, https…), and the host name and the port.

The clash between Same-Origin Policy and Ajax

This policy was useful for a while to protect against XSS, but with the advance of ajax, and requesting data and resources from different domains and sites, then this policy become an obstacle.

What is the solution?

The industry came up with many ideas:

1. Using a proxy page:

Just create a simple proxy page on the same web site that will forward all calls to the remote site, and return back the results.

2. For Silverlight and Flash only Crossdomain.xml:

Flash and Silverlight bypassed this problem by using a XML file that exists on the remote site host, which allow the technologies to get resources from it. We are not covering these, as these technologies are dying.

3. For IE8 only Cross-Domain Request

Microsoft tried to fix the problem on its own by introducing a new programming class called Cross-Domain Request (XDR) XDomainRequest. But it didn’t live long because JSONP emerged as a new de-facto standard, and after a short period W3C decided to create a new specification to solve this problem, which what we are going to cover next item. XDR stopped being supported on IE11 and after.

4. Using JSONP

JSONP became the standard for a while to fix the problem, before CORS was introduced. We are going to cover it in more details later in this post.

5. Using Cross-Origin Resource Sharing (CORS):

CORS is a new W3C Specs that allow browsers to query cross-domain sites. It is a new standard, which means it is the new preferable way to do cross site requests. After CORS was introduced, JSONP became less and less used.

How CORS work?

CORS requires coordination from the browsers and from the servers, and work has to be done on both side

Client side:

What should be done on the client side?
You don’t have to do anything except using a browser that support CORS, and almost all browsers now support CORS.
This is a list of browsers supporting CORS.
The browser that support CORS changed its implementation to XMLHttpRequest to use XMLHttpRequest2 (or some people call it XMLHttpRequest Level 2).
That means that your Javascript code you still create XMLHttpRequest, but the browser itself is implementing the specifications of XMLHttpRequest2.
Because XMLHttpRequest is implementing the new specs in the new browsers, so developers don’t have to do anything on the client side.

Service side:

What Should be done on the server side? On the server side, and specifically on the remote server that you are requesting resources/data from using the ajax call, you have to do more things.
You have to add a new setting to the header and set it to true the Header attribute is called Access-Control-Allow-Credentials, and it is used like this

1Access-Control-Allow-Credentials: true

This is a web site that shows you how to do it in different server side languages.

One more thing:

One issue that is related to the client side.
If you are using jQuery’s ajax before 1.5, then you might have to do some changes.
The reason you have to do some changes on jQuery side, because jQuery was adding its own security check to prevent XSS vulnerability. But with jQuery version 1.5 , jQuery introduced a new setting when calling ajax that is called crossDomain in version 1.5, which was introduced to handle JSONP settings.

How does JSONP work?

Let’s see who JSONP works, and let’s start with a remote service http://remote-server.com/Users/1234 that return this:

1// json data returned by the service http://remote-server.com/Users/1234
2
3{name: 'joe smith', age: 20}

There are two HTML tags that can bypass the Same-origin policy, which are the :

1<script> tag
2<img> tag.  

JSONP is using this feature to get access to the data from a remote site.
It wraps the service call with a <script> tag and dynamically inject the tag.
Here is an example of that:

1// wrapping the data with a script tag and dynamically appending the tag
2
3var script = $("<script />", {
4    src: "http://remote-server.com/Users/1234",
5    type: "application/json"
6  }
7);
8
9$("head").append(script);

But still we have no way to get handle of the json data.
In order to use the data above we have to return it in a meaningful way, by wrapping the data with a function that return the value, as follows:

1// json data wrapped with a function 
2// returned by the service http://remote-server.com/Users/1234
3function jsonCallback() {
4    return {name: 'joe smith', age: 20} ;
5}

We can enhance the previous code by making the function name as a parameter for the service and then from the client side we build a library that will do that wrapping magic.
jQuery has that ability to run Ajax calls with JSONP.
As an example:

1$.ajax({
2    url: "http://remote-server.com/Users/1234",
3    dataType: "jsonp", // Tell jQuery we're expecting JSONP
4    jsonp: "callback", // The name of the callback parameter
5    success: function( response ) {
6        // handle response
7    }
8});

JSONP requirements

As you noticed, JSON requires code on the server side, and on the client side.
On the client side most of Ajax libraries support JSONP out of box.
And on the server side, you need to wrap your returned data with a function.
Most web sites that provides API like flikr, facebook, ….etc. support JSONP.

Final conclusion

With the introducing of CORS, there is no need to use JSONP, but not every API provider is supporting CORS yet. That is changing with time, but in most cases you are going to find that if CORS is not supported then JSONP is supported.
If you want to still supporting IE 8, then you don’t have a choice than to use JSONP.