155

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.)

jpaugh
  • 5,719
  • 4
  • 33
  • 83
Rocketman
  • 3,264
  • 4
  • 23
  • 29
  • using eval ? then possible otherwise No. If nothing has been altered or changed in way backbone parses response then you should be safe – Deeptechtons May 03 '13 at 07:20
  • 11
    Generally speaking, you should never approach web security with the assumption that someone is going to be using a "modern" browser. – Luke May 03 '13 at 14:16
  • 7
    @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 May 04 '13 at 17:05
  • These vulnerabilities were found already in 2006 when gmail was hacked: http://jeremiahgrossman.blogspot.se/2006/01/advanced-web-attack-techniques-using.html – mikewse Mar 18 '14 at 09:02
  • 4
    @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 Jul 07 '15 at 17:00
  • @Frédéric You're making some dangerous assumptions, including that users who have administrative control or access to privileged information will not be some of the "lucky few" who use older browsers. This *may* be valid in some cases, but certainly should not be taken for granted. – jpaugh Apr 09 '18 at 21:34
  • @Frédéric And, FTR, I would *love* to have a pair of Chariot Skates, but could never justify the cost. ;-) – jpaugh Apr 09 '18 at 21:42
  • 2
    @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 Apr 09 '18 at 23:16

1 Answers1

117

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.

jpaugh
  • 5,719
  • 4
  • 33
  • 83
  • 29
    Back in 2009 Brendan Eich suggested that browsers not evaluate scripts served as application/json (https://bugzilla.mozilla.org/show_bug.cgi?id=376957#c75), which still seems like a good idea to me. –  Jun 02 '13 at 20:43
  • 2
    Note that blind POST CSRF is still possible using forms, particularly with the text/plain encoding, and needs to be defeated using tokens/nonces. –  Jun 02 '13 at 20:48
  • 1
    Yes to the POST CSRF. Thanks for all of your great info here. – Rocketman Jun 09 '13 at 17:18
  • @NickRetallack : I believe that IE has always used the built-in bindings for {} and [], but I suggest you check my test page. –  Oct 21 '14 at 19:21
  • Why did you reference the ES4 spec at all? ES4 was abandoned. We went directly from ES3.1 to ES5. – Jon Davis Feb 05 '15 at 18:52
  • Only because it was the chronological introduction of that requirement. –  Feb 05 '15 at 21:39
  • 5
    Your statement is correct when it refers to only simply overwriting of the Array constructor. Microsofts IE and Edge are still vulnerable to the UTF-7 JSON Hijacking though. Tested it recently (and for fun today again), and it still works. – user857990 Apr 06 '16 at 07:57
  • 2
    UTF-16BE as well, thanks to Gareth Heyes, http://blog.portswigger.net/2016/11/json-hijacking-for-modern-web.html – eel ghEEz Jun 28 '17 at 23:40