What Are "Top Level JSON Arrays" and Why Are They a Security Risk

What are top level JSON arrays and why are they a security risk?

This is because a few years ago Jeremiah Grossman found a very interesting vulnerability that affects gmail. Some people have addressed this vulnerabilty by using an unparseable cruft (Mr bobince's technical description on this page is fantastic.)

The reason why Microsoft is talking about this is because they haven't patched their browser (yet). (Edit: Recent versions of Edge and IE 10/11 have addressed this issue.) Mozilla considers this to be a vulnerability in the json specification and therefore they patched it in Firefox 3. For the record I completely agree with Mozilla, and its unfortunate but each web app developer is going to have to defend them selves against this very obscure vulnerability.

javascript - Why shouldn't the server respond with a JSON Array?

To avoid JSON Hijacking:

The fact that this is a JSON array is important. It turns out that a script that contains a JSON array is a valid JavaScript script and can thus be executed. A script that just contains a JSON object is not a valid JavaScript file.

For example, if you had a JavaScript file that contained the following JSON: {“Id”:1, “Balance”:3.14} And you had a script tag that referenced that file:
<script src="http://example.com/SomeJson"></script>

You would get a JavaScript error in your HTML page. However, through an unfortunate coincidence, if you have a script tag that references a file only containing a JSON array, that would be considered valid JavaScript and the array gets executed.

So allowing JSON to be returned as anything but an object would make it possible to return a JSON array that contained code that could be run on the client level (in a context where the client isn't expecting it to be runnable, could be malicious, etc). Only returning JSON objects prevents this from happening.

Django REST Framework: Is the top-level JSON array on an empty ListView a security risk?

Based on OWASP recommendation, you should always return a list with an object on the outside, but this appears to be only a vulnerability in older browsers as discussed in this post.

Sample Image

Nevertheless, it's better to adhere to the OWASP security recommendations to return the list nested inside an object if possible.

With Django DRF's generic ListModelMixin views, if you include all pagination settings, it will return an object with pagination attributes with the list under the results attribute.

# settings.py
REST_FRAMEWORK = {
...
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
}
# API response body

{
"count": 12,
"next": null,
"previous": null,
"results": [
...
]
}

JSON security best practices?

The main security hole from the blog (CSRF), is not JSON specific. It's just as big a hole using XML instead. Indeed, it's just as bad with no asynchronous calls at all; regular links are just as vulnerable.

When people talk about unique URLs, they generally DON'T mean http://yourbank.com/json-api/your-name/big-long-key-unique-to-you/statement. Instead, it's more common to make something else about the request unique; namely a value in the FORM post, or a URL parameter.

Usually this involves a random token inserted into the FORM on the server side, and then checked when a request is made.

The array/object thing is news to me:

Script-Tags: The attacker can embed a
script tag pointing at a remote server
and the browser will effectively
eval() the reply for you, however it
throws away the response and since
JSON is all response, you're safe.

In that case, your site doesn't need to use JSON at all to be vulnerable. But yeah, if an attacker can insert random HTML into your site, you're toast.

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>

What does JSON standard only allows one top level value?

You have to combine them as [ {Obj 1}, {Obj 2} ] if you want to use them in the same json file. Otherwise the json file will be containing two dictionary objects and it can only have one top level object.

How do I show only top level JSON entries that are not arrays or objects with jq version 1.3?

You're not really doing anything with the with_entries call there. But you probably should be filtering there. Filter the entries where the value's type is neither an array nor an object.

.person | with_entries(select(.value | type | . != "array" and . != "object"))

If you were on a more recent version of jq, you could filter using the scalars builtin which effectively does the same thing.

.person | with_entries(select(.value | scalars))


Related Topics



Leave a reply



Submit