Don't Break Your Backbone: XSS mitigation in Backbone.JS
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.
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.
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?
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.
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:
$('#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:
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.