Think you’ve protected your site against Cross-Site scripting attacks by escaping all the content that you’ve rendered? Thought about your javascript?
Here’s a neat bug that got us today. This example is contrived to show a point.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>XSS Example</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> <script> $(function() { $('#users').each(function() { var select = $(this); var option = select.children('option').first(); select.after(option.text()); select.hide(); }); }); </script> </head> <body> <form method="post"> <p> <select id="users" name="users"> <option value="bad"><script>alert('xss');</script></option> </select> </p> </form> </body> </html>
See the problem? Don’t worry, neither did the pair that worked on the javascript. But our QA showed us a neat little alert box!
It looks like the JQuery text() method returns the unescaped payload of the option, and the after() method then creates a nice little script tag. Nasty stuff.
How did we deal with the problem? This was our immediate fix:
// after() accepts a DOM element so lets create a text node select.after(document.createTextNode(option.text()));
Longer term fix – still open to suggestions.