157
votes

I am using Backbone.js and the Tornado web server. The standard behavior for receiving collection data in Backbone is to send as a JSON Array.

On the other hand, Tornado's standard behavior is to not allow JSON Array's due to the following vulnerability:

http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx

A related one is: http://haacked.com/archive/2009/06/25/json-hijacking.aspx

It feels more natural for me to not have to wrap up my JSON in an object when it really is a list of objects.

I was unable to reproduce these attacks in modern browsers (i.e. current Chrome, Firefox, Safari, and IE9). At the same time I was unable to confirm anywhere that modern browsers had addressed these issues.

To ensure that I am mislead neither by any possible poor programming-skills nor poor googling-skills:

Are these JSON Hijacking attacks still an issue today in modern browsers?

(Note: Sorry for the possible duplicate to: Is it possible to do 'JSON hijacking' on modern browser? but since the accepted answer does not seem to answer the question - I thought it was time to ask it again and get some clearer explanations.)

1
using eval ? then possible otherwise No. If nothing has been altered or changed in way backbone parses response then you should be safeDeeptechtons
Generally speaking, you should never approach web security with the assumption that someone is going to be using a "modern" browser.Luke
@Luke - See below comment to Reid. Great point in general - but I'm not asking a general security question. (My users will only be able to authenticate if they are using a modern browser in the first place.)Rocketman
@Luke, sometimes we have to move on and allow us to develop with modern patterns (such as REST in this case : obtaining data is a GET operation and should not be something else) without protecting against old threats if they now appear to apply only to a small audience. So this question is really valuable, to allow one to evaluate whether he can ignore this threat or not for his application case. At some point, user with very obsolete software are quite likely to have other kind of threats (malware) from which we will not be able to protect them anyway.Frédéric
@jpaugh, where do you see such assumptions? I only somewhat assume that those people with such obsolete software are "unprotectable" anyway. (About justifying the cost of my skates, I was already used to put one third of their price in carbon speed skates which were worn out in less than one third the time it is taking me to wear out my current skates. And anyway, I think they are worth it, provided you like riding them, which is my case.)Frédéric

1 Answers

119
votes

No, it is no longer possible to capture values passed to the [] or {} constructors in Firefox 21, Chrome 27, or IE 10. Here's a little test page, based on the main attacks described in http://www.thespanner.co.uk/2011/05/30/json-hijacking/:

(http://jsfiddle.net/ph3Uv/2/)

var capture = function() {
    var ta = document.querySelector('textarea')
	ta.innerHTML = '';
	ta.appendChild(document.createTextNode("Captured: "+JSON.stringify(arguments)));
	return arguments;
}
var original = Array;

var toggle = document.body.querySelector('input[type="checkbox"]');
var toggleCapture = function() {
    var isOn = toggle.checked;
    window.Array = isOn ? capture : original;
    if (isOn) {
        Object.defineProperty(Object.prototype, 'foo', {set: capture});    
    } else {
        delete Object.prototype.foo;
    }
};
toggle.addEventListener('click', toggleCapture);
toggleCapture();

[].forEach.call(document.body.querySelectorAll('input[type="button"]'), function(el) {
    el.addEventListener('click', function() {
        document.querySelector('textarea').innerHTML = 'Safe.';
        eval(this.value);
    });
});
<div><label><input type="checkbox" checked="checked"> Capture</label></div>
<div><input type="button" value="[1, 2]" /> <input type="button" value="Array(1, 2);" /> <input type="button" value="{foo: 'bar'}" /> <input type="button" value="({}).foo = 'bar';" /></div>
<div><textarea></textarea></div>

It overrides window.Array and adds a setter to Object.prototype.foo and tests initializing arrays and objects via the short and long forms.

The ES4 spec, in section 1.5, "requires the global, standard bindings of Object and Array to be used to construct new objects for object and array initializers" and notes in Implementation Precedent that "Internet Explorer 6, Opera 9.20, and Safari 3 do not respect either local or global rebindings of Object and Array, but use the original Object and Array constructors." This is retained in ES5, section 11.1.4.

Allen Wirfs-Brock explained that ES5 also specifies that object initialization should not trigger setters, as it uses DefineOwnProperty. MDN: Working with Objects notes that "Starting in JavaScript 1.8.1, setters are no longer called when setting properties in object and array initializers." This was addressed in V8 issue 1015.