Wednesday, September 19, 2012

XSS Attack - Prevent using JQuery Encoder


What is jqencoder

jqencoder is a jQuery plugin whose goal is to provide jQuery developers with a means to do contextual output encoding on untrusted data. As applications become more and more reliant on rich client technologies the need for client-side code to have the ability to properly escape untrusted data becomes exponentially more important.

Why Encode Output

Proper contextual output encoding is the primary and most effective way to combat Cross-Site Scripting (XSS) attacks. The premise is to use the escaping rules of the current context to not allow an attacker to break out of that context. The reason that output encoding is so important is because HTML, by nature, mixes code and data; thus an attacker can disguise code as data and that code can be executed unintentionally by other users.
There is a specific type of XSS Attack called DOM-Based XSS. The most dangerous factor of DOM-Based XSS is that attacker payloads are frequently not sent to the server; which means that any code on the server that is aimed at defending against XSS vulnerabilities will not be accessed, leaving your users wide open to attack. Client side contextual encoding is the answer to this problem. By encoding untrusted data in the correct context while dynamically building portions of the DOM or writing out Javascript, developers can effectively mitigate DOM-Based XSS attacks.
Client side contextual encoding also holds a prominent responsibility in dynamic AJAX applications – primarily those who load data from 3rd party services and display that data on their page. The client has no control over the integrity of the data being sent to them in most cases, so it is important than when rendering data from an untrusted source, such as a public webservice, that the developer be able to encode that untrusted data for use in the correct context.

Usage

Using the plugin is very simple, there are both static and instance methods to provide the right type of output encoding for your current situation. The plugin also provides a canonicalization method, which is imperative for detecting attacks that use double or multi encoding when handling data from an untrusted source. If you are dealing with data that may contain encoding, you can first canonicalize that data, then re-encode it for the appropriate context.
<script type="text/javascript">
    $.post( 'http://untrusted.com/webservice', { parameter1: 'value' }, function(data) {
        // Data returned may contain encoded data, so canonicalize the data to it's simplest
        // form prior to encoding it for use on the page.
        var cdata = $.canonicalize(data);
        $('#element').encode('html', cdata);
    });
</script>

Dependencies

    <script type="text/javascript" src="jquery-min.js"></script>
    <script type="text/javascript" src="Class.create.js"></script>
    <script type="text/javascript" src="jquery-encoding-0.1.0.js"></script>

Static Methods

canonicalize( String data, bool strict=true )

Canonicalization (also called normalization) is the act of reducing a string to it’s simplest form. For example, if the string %3CB%3E is passed into the canonicalize method, the value returned will be decoded into <b>. The most important part of this method is that it will detect if a string is passed in that contains either multiple encoding types, or double encoding, or both. The default behavior of the method is to raise an exception if it detects one of these scenarios. As a general rule, normal application operation should never pass data that is either double encoded or encoded using multiple escaping rules. Most definately, data that is provided by a user (such as a form field) will never contain data that fits that description.
    try {
        $.encoder.canonicalize(untrusted_data);
    } catch (e) {
        log.error(e);
        alert('An error has occured');
    }

encodeForCSS( String input, char[] immune )

This method allows developers to encode data specifically to be inserted into the style attribute of an element or as the value of a style attribute passed in through the jQuery .style() method.
    $.post('/service/userprefs', { user: userID }, function(data) {
        $('#container').html('<div style="background-color: ' + $.encoder.encodeForCSS(data['background-color']) + '">');
    });

encodeForHTML( String input )

This method allows developers to encode data specifically to be inserted between two tags in a document, either through the use of thehtml() method or by accessing innerHTML directly.
    $.post('http://untrusted.com/news/', function(data) {
        $('#element').html( $.encoder.encodeForHTML(data) );
    });

encodeForHTMLAttribute( String input, char[] immune )

This method allows developers to encode data specifically to be inserted between quotes in an HTML Attribute value.
    $.post('http://untrusted.com/profile', function(data) {
        $('#element').html( '<div width="' + $.encoder.encodeForHTMLAttribute(data.width) + '">' );
    }

encodeForJavascript( String input, char[] immune )

This method allows developers to encode data specifically to be inserted into a javascript event on an DOM element. This method will escape for a javascript context instead of a html attribute context.
    $.post('http://untrusted.com/profile', function(data) {
        $('#element').html( '<div onmouseover="someFunction(\'' + $.encoder.encodeForJavascript(data.event) + '\)">');
    }

encodeForURL( String input, char[] immune )

This method allows developers to encode data specifically to be inserted into a URL context. This is useful for encoding links with untrusted data in them.
    $('#dyn_link').html('<a href="/profile/' + $.encoder.encodeForURL(userID) + '">Link</a>');

Instance Methods

encode( Enum(html|css|attr) context, String input )

encode( Object opts )




How do you use jQEncoder?

Using the plugin is very simple, there are both static and instance methods to provide the right type of output encoding for your current situation. The plugin also provides a canonicalization method, which is imperative for detecting attacks that use double or multi encoding when handling data from an untrusted source. If you are dealing with data that may contain encoding, you can first canonicalize that data, then re-encode it for the appropriate context.
?
1
2
3
4
5
6
$.post( 'http://untrusted.com/webservice', { parameter1: 'value' }, function(data) {
    // Data returned may contain encoded data, so canonicalize the data to it's simplest
    // form prior to encoding it for use on the page.
    var cdata = $.canonicalize(data);
    $('#element').encode('html', cdata);
});
You can also use the static methods for dynamically building out your document instead of building elements using the DOM
?
1
2
3
4
5
6
$.post( 'http://untrusted.com/webservice', { parameter1: 'value' }, function(data) {
    $('#container').html(
        '<div class=' + $.encoder.encodeForHTMLAttribute($.encoder.canonicalize(data.class)) +
        '" style="background-color: ' + $.encoder.encodeForCSS($.encoder.canonicalize(data.background)) +
        '">' + $.encoder.encodeForHTML($.encoder.canonicalize(data.html)) + '</div>' );
}
Sandbox

Your payload will be injected directly into the div to the right using
?
1
2
3
4
$('#submit-entity-payload').click(function() {
    var payload = $('#entity-payload').val();
    $('#entity-container').html( $.encoder.encodeForHTML(payload) );
});




Your payload will be injected directly into the value of the class attribute of an inner div element within the div to the right. This will done using the following code
?
1
2
3
4
5
6
7
8
$('#submit-attr-payload').click(function() {
    var payload = $('#attr-payload').val();
    $('#attr-container').html(
            '<div id="attr-payload-val" class="' +
            $.encoder.encodeForHTMLAttribute($.encoder.canonicalize(payload)) + '">' +
            $.encoder.encodeForHTML( $.encoder.encodeForHTMLAttribute( payload ) ) +
            '</div>' );
});


Your payload will be injected directly into the value of the style attribute of an inner div element within the div to the right. This will done using the following code
?
1
2
3
4
5
6
7
8
$('#submit-css-payload').click(function() {
    var payload = $('#css-payload').val();
    $('#css-container').html(
            '<div id="css-payload-cal" style="background-color: ' +
            $.encoder.encodeForCSS($.encoder.canonicalize(payload)) + ';">' +
            $.encoder.encodeForHTML( $.encoder.encodeForCSS(payload) ) +
            '</div>' );
});


Your payload will be injected directly into the value of the alert function fired by the onmouseover attribute of an inner div element within the div to the right. This will done using the following code
?
1
2
3
4
5
6
7
8
$('#submit-js-payload').click(function() {
    var payload = $('#js-payload').val();
    $('#js-container').html(
            '<div id="js-payload-val" onmouseover="alert(\'' +
            $.encoder.encodeForJavascript($.encoder.canonicalize(payload)) +
            '\');">Hover over this div to see the result</div>'
    );
});

Your payload will be injected directly into the value of the href attribute of an inner a element within the div to the right. This will done using the following code
?
1
2
3
4
5
6
7
8
9
10
$('#submit-url-payload').click(function() {
    var payload = $('#url-payload').val();
    $('#url-container').html(
            '<a style="color:white;" href="#user-supplied-data=' +
            $.encoder.encodeForURL($.encoder.canonicalize(payload)) +
            '">' +
            $.encoder.encodeForHTML($.encoder.encodeForURL($.encoder.canonicalize(payload))) +
            '</a>'
    );
});

5 comments:

  1. Excellent! Thank you for the post!

    ReplyDelete
  2. Thank you so much Balaji. Truely Excellent.

    ReplyDelete
  3. I have one doubt, to make use these "$.encoder" to include sanitation/encoder functions do we need to include any security JS or library or is it part of the original JS. Current i have DOM XSS issue raised in the jQuery.min.js itself , how to fix things like this ?

    ReplyDelete
  4. How would one STOP attacks like these (copy paste in a browser):
    data:html,%3Cscript%3Ealert%28document.cookie%29%3C/script%3
    ---may result in an empty dialog box

    ReplyDelete