Wednesday, November 7, 2012

Remediation of XSS: Nested Contexts (part two)


In part one of this series I covered the correct use of JavaScript encoding and how this already covers the issue of the “nested” contexts. Now, onto a better solution – don’t use HTML Event attributes! Given the vulnerable code:
<div onclick="showError('<%=request.getParameter("error")%>')">
An error occurred, click here to see the details</div>
Instead of adding encoding to a complicated location within the DOM like the onclick event attribute - hook all of your events via JavaScript (example below uses JQuery):
<div id="errorBanner">An error occurred, click here to see the details
   <div id="errorDetails" style="display:none">
      <%=Encode.forHtml(request.getParameter("error")) %>
   </div>
</div>
<script type="text/javascript">
   $('#errorBanner').click($('#errorDetails').show());
</script>
The key here is to avoid placing dynamic data into "nested contexts" such as an event handler. This makes the remediation much simpler in many cases and lowers the amount of security knowledge a developer needs to understand how to fix the vulnerability.

The additional benefit of using JS to hook your events is that you can then externalize your JavaScript and define a Content Security Policy (CSP) for you site. CSP is by no means a magic bullet – but restrictive CSP policy can limit the damage potential of an XSS exploit.

Remediation of XSS: Nested Contexts (part one)

I have seen some solutions for XSS involving nested contexts that are not ideal. Partly because they are complicated and require a deep understanding of how the browser processes the HTML/DOM and they are likely inefficient; there are better solutions. This is the first post in a two part series.

First, what do I mean be nested contexts? Some examples would be writing dynamic data into an event handler such as onclick.
<div onclick="showError('<%=request.getParameter("error")%>')" >An error occurred, click here to see the details</div>
When the browser processes this it will first HTML decode the contents of the onclick attribute and then it will pass the results to the JavaScript Interpreter. As such, the advice I have seen (and previously given) is to apply “layered” encoding: 1) JavaScript encode and then 2) HTML Attribute Encode:
<div onclick="showError('<%= Encoder.encodeForHtml(Encoder.encodeForJavaScript( request.getParameter("error")%>')))" >An error occurred, click here to see the details</div>
Wow – that is completely unfriendly. Additionally, after thinking about this solution it seems unnecessarily complicated if you are using a robust JavaScript encoder (i.e. one that will JavaScript encode the '&' character) – then even though you are in an “HTML Attribute” context the following should be sufficient:
<div onclick="showError('<%= Encoder.encodeForJavaScript( request.getParameter("error")%>'))" >An error occurred, click here to see the details</div>
The reason that the above is sufficient is the Encode.forJavaScript will encode the '&' character so that when the browser HTML Decodes the attribute there is nothing to decode. However, this only applies to robust JavaScript encoders.

The encoder used above is from the OWASP Java Encoder Project.

Next post will cover refactoring the use of nested contexts rather than just encoding the data. As we will see, this has some very nice benefits.