JSON Hijacking in ASP.NET MVC 2

The following is an excerpt from ASP.NET MVC 2 in Action, a book from Manning appearing in bookstores in May.  The early access (MEAP) edition is available now on http://manning.com/palermo2.  Authors include Jeffrey Palermo, Ben Scheirman, Jimmy Bogard, Eric Hexter and Matt Hinze.  Technically edited by Jeremy Skinner.


JSON (pronounced like the English name, Jason) hijacking is a rare hack similar to XSRF, except it’s targeted to request secure JSON from vulnerable applications. The JSON hijacking process involves several steps:

1. A conspiring site, via JavaScript, instructs the victim’s browser to request some secure JSON data from another site.

2. The evil JavaScript receives the JSON data.

3. If the JSON is formatted as an array, the evil script can exploit browser JavaScript processing code to read the JSON data and transmit it back to the attacking site.

Allow JSON via POST only

The solution to this exploit offered by ASP.NET MVC 2 is to only accept requests for JSON data by HTTP POST requests, rather than by GETs. This is baked into and enforced by the standard JsonResult action result that ships with the framework. If we were to request data to be returned by JsonResult with a GET request, we wouldn’t receive the JSON data.

Listing 11.12 shows how we must issue a POST from JavaScript code requesting JSON data.

Listing 11.12 Requesting JSON data via POST
<script type="text/javascript">
    $.postJSON = function(url, data, callback) {
        $.post(url, data, callback, "json");
    };

    $(function() {
    $.postJSON('/post/getsecurejsonpost',
        function(data) {
            var options = '';
            for (var i = 0; i < data.length; i++) {
                options += '<option value="' +  #|2
                data[i].Id + '">' + data[i].Title +
                '</option>';
            }
            $('#securepost').html(options);

        });
    });
</script>

 <h2>Secure Json (Post)</h2>
  <div>
    <select id="securepost"/>
 </div>

Listing 11.12 uses the jQuery JavaScript library to craft a special POST request for our JSON data.  When the results are returned, the function populates the select list with them.

Override defaults for GET access

The problem with this approach isn’t technical-this works and it prevents JSON hijacking. But it’s a workaround that’s sometimes unnecessary and can interfere with systems developed using the REST architectural style.

If this approach causes problems, we have additional options. First, we can explicitly enable JSON requests from GETs with the code shown in listing 11.13.

Listing 11.13 Directing JsonResult to accept GETs
[HttpGet]
public JsonResult GetInsecureJson()
{
    object data = GetData();

    return Json(data, JsonRequestBehavior.AllowGet);
}

This will allow our action to respond to normal JSON GET requests. Finally, we can scrap JsonResult itself, instead using an action result to return only non-vulnerable, non-array formatted, JSON.

Modifying the JSON response

The code in listing 11.14 shows a special action result that wraps vulnerable JSON data in a variable, d.

Listing 11.14 Creating a SecureJsonResult encapsulates serialization logic
public class SecureJsonResult : ActionResult
{
    public string ContentType { get; set; }
    public Encoding ContentEncoding { get; set; }
    public object Data { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
         if (context == null)
         {
              throw new ArgumentNullException("context");
         }
         HttpResponseBase response = context.HttpContext.Response;
         if (!string.IsNullOrEmpty(ContentType))
         {
              response.ContentType = ContentType;
         }
         else
         {
              response.ContentType = "application/json";
         }
         if (ContentEncoding != null)
         {
              response.ContentEncoding = ContentEncoding;
         }
         if (Data != null)
         {
              var enumerable = Data as IEnumerable;
              if (enumerable != null)
              {
                    Data = new {d = enumerable};
              }
              var serializer = new JavaScriptSerializer();
              response.Write(serializer.Serialize(Data));
         }
    }
}

This action result encapsulates the tricky code to output the proper JSON, and it works well. The downside to this approach is that we must use this d variable in our JavaScript code. Listing 11.15 shows consuming the serialized data using jQuery.

Listing 11.15 Consuming SecureJsonResult with jQuery
$(function() {
$.getJSON('/post/getsecurejson',
    function(data) {
        var options = '';
        for (var i = 0; i < data.d.length; i++) {
            options += '<option value="' +
            data.d[i].Id + '">' + data.d[i].Title +
            '</option>';
        }
        $('#secure').html(options);
    });
});

Using this technique, we can still use GETs to retrieve our JSON data, but the JSON is secure because it’s never just an array-any arrays are wrapped in a d variable. We just must be sure to access values through the d variable.

This unconventional code can be confusing. We recommend using the default behavior of using HTTP POST requests to retrieve JSON data. If that becomes a problem, you can switch to this technique.

About these ads
This entry was posted in ASP.NET MVC. Bookmark the permalink.

3 Responses to JSON Hijacking in ASP.NET MVC 2

  1. Pingback: Problems with avoiding JSON hijacking with MVC3's AntiForgeryToken, or similar token validation | Q&A System

  2. Pingback: Problems with avoiding JSON hijacking with MVC3's AntiForgeryToken, or similar token validation | Q&A System

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s