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.