jQuery UI autocompletion and Javascript hijacking

By Abhijit Menon-Sen <ams@toroid.org>

2011-03-05

I was pleased to discover that the jQuery UI library includes an autocomplete widget that looks good and works well, and I started using it straightaway to liven up my search boxes. The simplest way to use it is to return an array of JSON objects matching a query from /some/url, and feed the client the following bit of Javascript:

$('#query').autocomplete({source: '/some/url'});

All was well, until I came across this paper on Javascript Hijacking. A quick summary: someone includes a <script> tag on their web site pointing to http://your.serv.er/that/url, and write some Javascript to steal the data (by redefining the array constructor, or overriding the property setter for objects). Due to the very specific circumstances of this attack, it is often misunderstood or ignored as "just another XSS attack", but it can lead to leakage of confidential data. The attack was originally demonstrated by Jeremiah Grossman to steal contacts from someone's GMail account.

The paper outlines a few possible solutions: deal with it like any other cross-site request forgery by requiring the request to include a random unguessable (which means using POST or including the token in the URL), or prefixing the response with "while(1);" or some other statement that would cause the browser to fail to execute the code (e.g. wrap it inside a comment).

Tony Cook pointed out to me that the attack hinges on the fact that an array literal is a valid statement in Javascript, but an object literal by itself is not. This suggests that returning the matches as an object rather than array will defeat the attack:

{"matches": [{"label": "one", "value": 1}, ...]}

Although I can't be certain that no Javascript implementation will treat this as a valid statement, I like this approach better than using POST to fetch a completion list or extending CSRF protections to GET requests. Fortunately, jQuery UI's autocomplete widget is more than flexible enough to accommodate an array of matches wrapped in an object:

$('#query').autocomplete({
    source: function (request, response_cb) {
        $.getJSON(
            '/some/url', {term: request.term},
            function (data) {
                response_cb(data.matches);
            }
        );
    }
});

Unfortunately, this is already much more code than people should have to write to handle this case, and it doesn't even handle errors (if any do occur and are not properly handled, the autocomplete widget will behave strangely). I think jQuery UI should handle this case by default.

This code can easily be adapted to cope with a "while(1);" prefix—treat the response as a string and remove the prefix, then eval the remainder and pass the resulting object to the response callback. If someone can convince me that an object literal is insecure—something of which I am quite willing to be convinced—then I'll change my code to do that. (If this seems a too-casual attitude, it's because none of my autocompletion data are confidential. Defeating casual attacks is fine for the moment.)