Showing posts with label dom based xss. Show all posts
Showing posts with label dom based xss. Show all posts

3.07.2011

New Encoding - Property Aware Contextual Encoding

After some conversations over Twitter with the the XSS Ninja known as Gareth Heyes regarding different escaping needs that went even further than just having the context itself. Basically, the gist of the conversation asserted that different escaping rules applied to different CSS properties, for instance the background-color property accepts Hexadecimal color codes (#CCCCCC) or rgb color (rgb(100,100,100)) formulas as well as plain-text well-known color keywords (blue) - this is drastically different than what would go into something like say the width property - which would simply be a fixed size or percentage. It was at this point that we came to the conclusion that jquery-encoder should use the property name that is being encoded for to determine the correct escaping syntax.

The new API for the property aware encodeForXXX methods follows

  • encodeForCss(property,data,omitPropertyName)
    Returns the encoded property: value pair, escaped in the context of the passed in property. Banned properties are the behavior family (behavior,-moz-behavior,-ms-behavior) as they are not safe to be set using untrusted data and allow for script injection by definition. Values that contain the expression keyword will also be rejected as unsafe, as this is the equivelent of calling the javascript eval within a style context. If the optional omitPropertyName is true the function will return only the value encoded for the passed in property.
  • encodeForHTMLAttribute(attribute,data,omitAttributeName)
    Returns the encoded attribute="value" pair, escaped in the context of the passed in attribute. Banned attributes are href and src as those should be encoded using the encodeForUrl function. The javascript event hooks on* are also banned as they should be set using the encodeForJavascript function. The style attribute should be set using the encodeForCSS function. If the optional omitAttributeName parameter is true, the function will return only the value encoded for the passed in attribute.

In all cases, the property/attribute names are canonicalized prior to encoding to validate and get the escaping context for that property (or the default if there is no specific context specified)

This was a somewhat difficult decision to make, simply because it is mixing in a bit of validation with the output encoding control - which is not necessarily ideal from a pure design standpoint. I felt however, that this was a necessary evil in order to ensure correct encoding/escaping context and get the most value from the plugin.

Please continue to send me your thoughts and ideas for the plugin - I plan on releasing it to the general public through the jQuery plugin repository within the next couple weeks so any feedback from the community leading up to the release of the plugin will only make it stronger!

As always, the latest version of the plugin is available from my github
https://github.com/chrisisbeef/jquery-encoder

The sandbox (which will be updated with the latest version today) is available on my site:
http://software.digital-ritual.net/jqencoder/

2.21.2011

jQuery-Encoder updated

I have made several updates to the jqencoder plugin over the weekend and thought I would share a little about them quickly.

Plugin Readme: http://bit.ly/ie4J04

First, and most importantly - I have added a series of static methods (that look similar to the methods on the Encoder interface for ESAPI) to perform particular contextual encoding tasks - specifically when building html dynamically rather than building elements up using the DOM.

  • encodeForHTML
  • encodeForHTMLAttribute
  • encodeForCSS
  • encodeForURL
  • encodeForJavascript

Each of these methods can be accessed under the static $.encoder context.

$.post('http://untrusted.com/external_profile', function(profile) {
      $('#widget').html('<div id="untrusted_widget" width="' + 
                        $.encoder.encodeForHTMLAttribute(profile.width) + 
                        '" onmouseover="' + profile.callback + "(\'' +
                        $.encoder.encodeForJavascript(profile.parm) + 
                        '\')">' + $.encoder.encodeForHTML(profile.data) + 
                        '</div>');
   }

In addition, the $.canonicalize method has also been moved into the $.encoder context.

$('#phonenumber').blur(function() {
      validatePhoneNumber($.encoder.canonicalize(this.val());
   });

The third, and final big change over the weekend - was solidifying the ES5 immutable objects protection. If it is supported by the browser, the $.encoder object will be frozen, sealed, or non-extensible (in that order of priority) to protect the encoding and canonicalize functions themselves from being tampered with at runtime. At this point in time, Chrome has implemented Object.freeze in the latest release version, Mozilla has implemented it in Firefox 4 and Microsoft have implemented it in IE9. Safari shows no indication of implementing it, and neither does Opera.

Now, I pose a question to the developers that may use this plugin. Is there a need to keep the instance method $.fn.encode? It seems to me that due to the nature of setting DOM element properties via Javascript, that this is not really needed at all. So, should I nuke it?

I end this post with a final thought (continuing from my above conversation of Object.freeze)

I strongly recommend that developers start taking the initiative to make their custom JS objects immutable, and also recommend making framework objects immutable as well. If you were to (using jQuery) issue the following in your onready handler

$(document).ready(function(){
   if ( Object.freeze ) $ = Object.freeze($);
   // .. initialize page below here
});

It seems to me, this could eliminate a lot of potential vulnerability exploitation of bugs in framework code. What are your thoughts?

Also, why not consider the following:

var lock_objs = [ String.prototype, 
                     Array.prototype, 
                     Function.prototype, 
                     Object.prototype ];
   for (var i=0;i<lock_objs.length;i++) lock_objs[i] = Object.freeze(lock_objs[i]);