Scott Seely

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

News

  • INETA Community Champions
How JSONP works (and some bits about implementing it in WCF)

In the world of the web, we have lots of security concerns. One of the concerns lies with cross site scripting, XSS. From a high level, XSS is any occasion where data is sent from code on a page from one site to another site. The code is usually via JavaScript, though flash and Silverlight are included in the terminology and mitigations. One way around this that is generally viewed as safe is JSON with Padding, aka JSONP. The way JSONP works is this: you pass an HTTP GET request to retrieve a resource from another site which you do not control. The request contains two pieces of information:

1. The resource you want to retrieve.

2. The callback function that should be executed when the resource is returned.

What makes this safe? The callback function is always something that you control. It is assumed that if your callback does bad things, so be it; that problem existed without involving JSONP.

There is a convention around JSONP. The URL that you call needs to understand a querystring parameter named callback, which identifies the function to call when the method returns. How does this work? Assume we have a resource that, when no callback is specified, returns the following when requesting the resource at

http://localhost/WcfJsonp/jsonp/names:

[{"FirstName":"Scott","LastName":"Seely"},

{"FirstName":"Aaron","LastName":"Skonnard"},

{"FirstName":"Matt","LastName":"Milner"}]

With JSONP, we would like a GET of the above data to execute a function on our page named updatePeople. JSONP indicates that we do this with a request for the resource at http://localhost/WcfJsonp/jsonp/names?callback=updatePeople. In so doing, the resource is now returned as:

updatePeople(

[{"FirstName":"Scott","LastName":"Seely"},

  {"FirstName":"Aaron","LastName":"Skonnard"},

  {"FirstName":"Matt","LastName":"Milner"}]);

This causes the function, updatePeople, to be called as soon as the resource is retrieved.

How does this work? Most of us will use jQuery to get the job done and not think about things. But, without jQuery, JSONP is still possible. I’ll first show you how this works by hand, then with jQuery. jQuery eliminates a number of potential bugs, so I recommend reading the by hand portion solely to understand things—don’t implement it in production code!

Assume you have the following HTML body:

 

<body>
    <div>
      <input type="button" onclick="getPeopleJsonp();" 
             value="Get people" />  
    </div>
    <div id="people"></div>
    <script type="text/javascript" 
      src="/wcfjsonp/Scripts/jquery-1.4.1.min.js"></script>
    <script type="text/javascript">
        function updatePeople(data) {
          var innerHtml = "";
          for (i = 0; i < data.length; ++i) {
            innerHtml += data[i].FirstName + ' ' + 
                         data[i].LastName + '<br/>';
          }
          $("#people").html(innerHtml);
        }

        function getPeopleJsonp() {
          var innerHtml = "<script type='text/javascript' " + 
                          "src='http://scottseely-xps/wcfjsonp/" +
                          "jsonp/names?callback=updatePeople'><\/script>";
          $("#inject").html(innerHtml);
        }
    </script>
    <div id="inject"></div>
</body>

What is happening here? When someone clicks on the button, the page calls getPeopleJsonp. That code updates the content of the div whose id is inject to contain a script tag. The script tag points to a JSONP endpoint and passes the callback parameter indicating to call the function updatePeople upon success. That script is then evaluated, executes a local JavaScript function, and returns. Every time that same script is reinjected, the same behavior occurs.

Now, jQuery provides a more succinct mechanism for doing the same thing. Instead of having the previous two functions and updating the DOM ourselves, we would write this:

 

function getPeopleJquery() {
  $.ajax({
    dataType: 'jsonp',
    url: "http://scottseely-xps/WcfJsonp/jsonp/names",
    success: function (data) {
      var innerHtml = "";
      for (i = 0; i < data.length; ++i) {
        innerHtml += data[i].FirstName + ' ' + data[i].LastName + '<br/>';
      }
      $("#people").html(innerHtml);
    }
  });
}

The code to inject information into the DOM still occurs, as does the creation of a callback function. What jQuery does is it adds script tag to the DOM only until the callback (success function) is done executing. The tag is added to the header. It also adds a function to the window object which is called on return. The URL jQuery constructs looks like http://localhost/WcfJsonp/jsonp/names?callback=jsonp1283609644773. What it actually does is add the following:

blog-tagadded

Within the window object, a new function is added:

expandos

jsonp1283609644773 is just an object living on the window object and is a dynamically added function. When the function is done executing, it will remove itself from the window object, leaving a slot by the same name with a value of undefined. What does the function look like when it is alive?

function(q){n=q;b();d();z[i]=v;try{delete z[i]}catch(p){}A&&A.removeChild(B)}

So, yeah, not too intelligible, but it does appear to handle things just fine. b(), d(), and the other functions are all part of the minified jQuery library, meaning that it all makes sense in the context of the containing object. The call to b() is contains code to call the success function. Everything else is cleanup code (at least from what it looked like to me…). To make this whole thing work in WCF, you need to do a couple simple things:

1. Only use WebGet to fetch resources.

2. Make sure the resource is hard-coded to respond in JSON. Otherwise, the response will appear as XML and the callback code won’t execute.

3. Make sure your binding supports JSONP.

4. Make sure your service requires ASP.NET compatibility.

For example, to get the names, I have the following WCF code:

[ServiceContract]
[AspNetCompatibilityRequirements(
  RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class JsonpDemo
{
  [OperationContract]
  [WebGet(UriTemplate = "names", ResponseFormat = WebMessageFormat.Json)]
  public PersonName[] GetNames()
  {
    return new PersonName[]
              {
                new PersonName {FirstName = "Scott", LastName = "Seely"},
                new PersonName {FirstName = "Aaron", LastName = "Skonnard"},
                new PersonName {FirstName = "Matt", LastName = "Milner"}
              };
  }
}

Configuration has:

<system.serviceModel>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  <standardEndpoints>
    <webHttpEndpoint>
      <standardEndpoint crossDomainScriptAccessEnabled="true" 
                        automaticFormatSelectionEnabled="true"/>
    </webHttpEndpoint>
  </standardEndpoints>
</system.serviceModel>

 

The crossDomainScriptAccessEnabled flag turns on the ability to correctly parse and respond to the presence of the callback parameter in the query string.

Finally, I added a route to handle the service within my Global.asax:

var factory = new WebServiceHostFactory();
routes.Add(new ServiceRoute("jsonp", factory, typeof(JsonpDemo)));

I was looking at the technology only to evaluate how to use it and to understand, a bit more deeply, how JSONP actually works. I found myself looking for a post that explains the magic, but most of the information I found just said “use jQuery.” Handy—sure. But that didn’t help me understand all the magic goodness that jQuery was doing for me.


Posted 09-07-2010 8:00 AM by Scott Seely

[Advertisement]

Comments

Michael C. Neel wrote re: How JSONP works (and some bits about implementing it in WCF)
on 09-07-2010 9:25 AM

Good post Scott - I've been using JSONP by hand a while now, and had no idea there was support in JQuery!

Chris Tavares wrote re: How JSONP works (and some bits about implementing it in WCF)
on 09-07-2010 4:28 PM

How is this safe exactly? Isn't this still eval'ing directly data sent from the server?

I can see the window for exploitation is smaller, but I'm still not convinced it's closed. Are there some references for this technique that I can use to reassure myself?

Scott Seely wrote re: How JSONP works (and some bits about implementing it in WCF)
on 09-07-2010 5:49 PM

Yes, you are evaling data sent from the server-- exactly the same as any time your code invokes any JSON service.

This is 'safe' only if you consider the fact that you can pen-test your own code that you publish. If you allow JSONP from untrusted sources, you will have malicious stuff happening. You need to trust the other party who is publishing data to be eval'd by your own code.

I found a decent discussion of the pros/cons of using JSONP here: erlend.oftedal.no/blog, erlend.oftedal.no/blog

At the end of the day, if you don't trust the site you are invoking, you have to think about why you are using a JSONP against that untrusted location.

Chris Tavares wrote re: How JSONP works (and some bits about implementing it in WCF)
on 09-07-2010 6:50 PM

Actually, one of the virtues of JSON is that it's easy enough to parse that you don't have to eval() it, you can run it through a separate parser instead and guarantee you don't get any junk in the data. I'd never  eval a JSON string coming back from any service.

As far as the trusted thing goes, well, I do trust sites, but I can't trust that they won't get hacked by somebody malicious after the fact. Or that their DNS record won't get hijacked.

Scott Seely wrote re: How JSONP works (and some bits about implementing it in WCF)
on 09-07-2010 7:12 PM

Chris, it appears that you do not understand the reason for JSONP existing. Normal JSON calls have a same domain origin policy as implemented by modern browsers. If you want to get JSON from another domain, you are out of luck-- XMLHttpRequest won't help, nor will anything else.

JSONP works around this by allowing a browser to request data from a different domain and then evaluate the data once it returns. The notification that the data has returned is handled via the callback. What is actually happening is you are including a new script in your page. That script may do many things, so it isn't really an 'eval'. It's more of a new script being injected. For convenience, you pass a callback function which is then called once the code is loaded. The returned script could navigate to a new URL too-- you have no guarantees about what the caller does. (Again, the article is about how it works for well-behaved JSONP providers, not malicious ones.)

You can also request data via a proxy on the host, where the request is proxied by your own server. OpenSocial uses this for makeRequest requests. This would satisfy your needs. However, we are talking about a page a developer creates and assume they are doing things that make sense to them based on other partnerships, which is all outside the scope of this article.

Would you find an article on security concerns to be interesting?

Nick Swan wrote re: How JSONP works (and some bits about implementing it in WCF)
on 12-11-2010 7:27 PM

I had an issue with "Cross domain javascript callback is not supported in authenticated services.", had to add <authentication mode="None"/> to the web.config of my service project.

This is great for webget, but what about webinvoke? I want to pass some data back across domains to be written back so if I'm following REST I need to use POST? Or should I just hack it and use GET as POST doesn't work across domains?

Scott Seely wrote re: How JSONP works (and some bits about implementing it in WCF)
on 12-11-2010 7:46 PM

JSONP is HTTP GET only.

What you are describing is cross site scripting, which most browsers prevent. To do more elaborate things, you can use Flash or Silverlight to handle the interactions. Both technologies integrate nicely with JavaScript and HTML.

Pascal Batzli Jr wrote re: How JSONP works (and some bits about implementing it in WCF)
on 12-18-2010 11:58 AM

First of all, great article. Thanks so much. Please help me in a question I have: why must I make sure your service requires ASP.NET compatibility? What's the difference? And if it is, must I host it in IIS?

Tks so much

Jasemin wrote re: How JSONP works (and some bits about implementing it in WCF)
on 05-31-2011 8:31 PM

YMMD with that anewsr! TX

Dave wrote re: How JSONP works (and some bits about implementing it in WCF)
on 10-28-2011 9:00 PM

I ran into issues when trying to add windows/ntlm authentication to the wcf service.  Note; due to security issues jsonp is will not work if you need to secure your service.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)