21 May, 2015

Don't Break Your Backbone: XSS mitigation in Backbone.JS

by Anand Vemuri & Mike McCabe

Cross-Site Scripting (XSS) has maintained its status as one of the most common application security vulnerabilities since the infancy of the internet. There are three types of XSS, and each type can occur in multiple contexts. In addition, numerous modern front-end frameworks and libraries each offer their own custom solutions. Naturally, proper XSS mitigation poses a unique, application-specific set of challenges. We recently discovered a common library-specific issue during a recent assessment of an application that used Backbone.js.

Backbone.js (“Backbone”) is one of the most popular JavaScript libraries for front-end application development. It features models for data key values, collections for working with sets of models, and views with event handling. Backbone has become ubiquitous, with companies  like Airbnb, Groupon, Pandora, and more implementing it for their web applications. Backbone is a flexible library to help glue together front-end data management and the display of data. As JavaScript libraries and frameworks push more and more app functionality to the browser, they push more risk and responsibility there as well. JavaScript frameworks can (and do) introduce many of the vulnerabilities we see in back-end applications.

We can’t show client code in this post, so we’ll use a sample Backbone app, backbone-jax-cellar. This is an open source project to demo Backbone with a Java back end.

This application is relatively simple, with basic functions to list, add, and delete wines from the app. The app utilizes Backbone to speed up response time and create a more reactive interface. We can modify the data and the app updates without refreshing the screen, reloading the DOM, or waiting for the server-side application to respond.

Scanners

We can start assessing the security of the application by running a few JavaScript scanners. Most JavaScript scanners are programmed to pick up on traditional web application XSS but seldom detect framework/library-specific XSS vulnerabilities. With that being said, when we ran the source code of the wine cellar app against ScanJS and JSPrime, we got the following results:

ScanJS did not find any vulnerabilities in the application. JSPrime suggested a potential instance of XSS, but upon further inspection, we saw that the scanner pointed to a non-vulnerable sink. It would be very tempting for developers to stop worrying about security at this point and carry on with the software development process.

Almost every security professional would begin dynamically testing this application for XSS by injecting a simple payload such as <script>alert('xss')</script> into input fields and seeing how the application responds. Even though the scanners claim the application is not vulnerable, we’re able to get our payloads to execute rather effortlessly:

Clearly, this app is vulnerable, but how can we fix this issue?

The Problem

Looking closer at the source code, we see this scenario is a bit more complicated than it seems. The script fires immediately once we click the Save button but also each subsequent time we reload the page. The following image delineates what is happening from the lens of an attacker:

As we can see, there are actually two separate cases of XSS at hand, Stored and DOM. We’ll have to tackle each one separately.

HTML Context - The Escape Function

In this instance, we have Stored XSS in an HTML context. A quick search on the Backbone.js home page reveals that a Backbone function exists for escaping models. The online documentation states:

Similar to get, but returns the HTML-escaped version of a model’s attribute. If you’re interpolating data from the model into HTML, using escape to retrieve attributes will prevent XSS attacks. After applying the escape function on the model attributes before they are rendered on the page, we notice the once malicious <script>alert(1)</script> is now innocuous.

JavaScript Context DOM XSS

We now have to focus on fixing the DOM-based XSS issue. With the existing fix in place, our diagram looks like this:

Fortunately, it appears the DOM XSS is not very exploitable, as it only executes on the attacker’s machine and not on the victim’s. However, in the interest of comprehensive security, it’s important to secure this as well.

As seen in the following code, the DOM XSS occurs because the saveWine function in winedetails.js creates a JSON object setting the model attributes to the user’s input values:

When $('#name').val(), evaluates, the payload executes in the browser.

This is an interesting type of XSS characteristic of Backbone applications, as it is DOM-based XSS with an HTML-context payload to trigger. Consequently, traditional solutions for XSS mitigation are not effective as they do not properly mitigate the full issue.

The most obvious solution for preventing this vulnerability is to escape the model attributes before they are stored. We can try using the underscore escape function to do this, and it appears this solution works in this context:

This being said, if the model attribute data were to be used in any other non-HTML context such as JavaScript or CSS, then it would be crucial to un-escape the data before storing it; otherwise, the string would be stored in its HTML-escaped form, i.e &lt;script&gt;alert(1)&lt;&#x2F;script&gt;.

Conclusion

The advent of more technologies increasing client-side responsibilities allows for new attack vectors. In this post we presented a very interesting instance of this in an application that leverages the Backbone.js library. Backbone’s integration with the DOM allows for a few XSS vectors that are not easily detectable via modern scanners. Therefore, it is very important that developers exercise proper use of escape functions to secure their applications. Preventing attackers from breaking Backbone applications puts us one step closer to strengthening the security posture of the internet.