Is JSON Hijacking still an issue in modern browsers?

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.

Leave a Comment