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">&lt;script&gt;alert(&#x27;xss&#x27;);&lt;/script&gt;</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.