In my recent article on Rails, JQuery, Unobtrusive JS and Graceful Degradation I implemented the Ajax logic for adding a new entry using JSON returned from the server to build the HTML in JavaScript. An attentive reader brought to my attention the cross-site scripting vulnerability that is possible with this implementation and I think this would be a good time to address the issue and open the comments to other options.
First of all let’s look at the most obvious attack vector, which is pretty simple: a malicious user would enter a script into the person name field and save that and then it would execute the script when the person is rendered in the person list. First I verified that this attack vector worked, and yes it did work, so then I considered where the value entered by the user needed to be sanitized. It’s pretty straightforward to sanitize the rendering when Rails is involved, just add the h() helper before the name output in the listing, so line 4 of index.html.erb becomes:
This is a fairly standard idiom in Rails (not necessarily the best way to sanitize user input, but the most common way to do it). This fix only fixes the problem when the page is rendered by Rails, but it does not address the issue for items that are added via Ajax, and here is where things get a bit tricky.
You’d think that sanitizing user input in JavaScript would be fairly easy, and to be clear you can do it with the JavaScript escape() function, however this results in some pretty ugly percent-encoded content (which may or may not matter to you. I was curious though whether there was something similar to the h() helper in Rails available in JavaScript. There is but finding it proved to be a bit of a challenge.
What I ended up doing what searching Stack Overflow for solutions that others had come up with and eventually landed upon this bit of code: http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/plugin/html-sanitizer.js. Unfortunately this bit of code by itself doesn’t work unless you have another JS library (html4) and that library requires building with Ant. Fortunately additional searching led me to a minified version with everything already compiled. I’ve included this minified library as html-sanitizer-minified.js and added a call to html_santize before rendering the person name in the JavaScript as such:
..and it works. I’ve deployed this fix to http://rails-jquery-unobtrusive.heroku.com/ and have updated the github repo so you can see the changes in action.
So is this the best solution for handling user input and avoiding XSS when you want to build HTML in JavaScript? I’m not sure. It’s probably not the only solution so feel free to add yours in the comments below. Also, consider this a warning if you are developing JavaScript code that builds and renders HTML: tainted user input is a threat and you need to deal with it.





#1 by Korny on February 21, 2010 - 11:28 pm
Did you check the javascript exploit on JQuery 1.4?
My understanding is that JQuery 1.4 should sanitize the JSON you get back – so while you could store a script in the form, it shouldn’t be returned as executable code.
http://www.neeraj.name/blog/articles/895-handling-json-parsing-natively-in-jquery-1-4-and-what-changed-from-jquery-1-3
(I haven’t actually tried this myself!)