Tuesday, June 4, 2013

JSON Hijacking! Taint your JSON!

JSON Hijacking is an attack where your JSON results are directly called by a malicious site. We are assuming that a target user with valid authorization has been tricked into visiting the malicious page. Anyway, the attack is based on the browser running your JSON as JavaScript. The malicious site can override basic JavaScript setters to inject code into your code running and essentially get around cross domain protections. More info at http://haacked.com/archive/2009/06/24/json-hijacking.aspx and http://www.thespanner.co.uk/2011/05/30/json-hijacking/.

There are a couple defenses. One is to force all your JSON requests to be POSTs. This is what MVC tries to get you to do, but this basically abandons the HTTP protocol in order to get some security. Another way to handle this issue is response tainting as detailed in Ajax Security. I followed the advice in this book and am now attaching "for(;;);" to my JSON results. This involved four things.

1) Class inheriting from JsonResult that adds the taint.

/* This is a modified version of JsonResult. The purposes is to do response 
tainting on Json. The client will need to be able to handle this response 
tainting. The "JsonRequestBehavior" property is ignored since response tainting * and denying GETs is redundant.
*/
public class TaintedJsonResult : JsonResult
{
    private const string Taint = "for(;;);";

    public override void ExecuteResult(ControllerContext context)
    {
        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = string.IsNullOrEmpty(ContentType) ? 
            "application/json" : ContentType;
        if (ContentEncoding != null)
        {
            response.ContentEncoding = ContentEncoding;
        }
        if (Data == null)
        {
            return;
        }
        JavaScriptSerializer scriptSerializer = new JavaScriptSerializer();
        response.Write(Taint);
        response.Write(scriptSerializer.Serialize(Data));
    }
}

2) On the client side you have to remove the taint. Assuming you are using JQuery...

$.ajaxSetup({
    dataFilter: function (data, type) {
        var taint = "for(;;);";
        if (data.lastIndexOf(taint,0) === 0) {
            return data.substr(taint.length);
        }
        return data;
    }
});

3) In my base Controller I add a convenience method mirroring "Json"

protected TaintedJsonResult TaintedJson(object data, string contentType = null, 
    Encoding contentEncoding = null)
{
    return new TaintedJsonResult()
    {
        Data = data,
        ContentType = contentType,
        ContentEncoding = contentEncoding
    };
}

4) Now when I have a Controller method that normally has a call like "return Json(objects)" I can instead call the following:
return TaintedJson(objects);


No comments:

Post a Comment