XSS Prevention From Unsanitized Server Input

XSS Prevention From Unsanitized Server Input

matt_matt_ Posts: 3Questions: 1Answers: 0
edited December 2015 in Free community support

I have been working recently with DataTables and server sided processing. Some of the test data I was using was not sanitized ie:

<script>alert('xss');</script>

and discovered DataTables directly injects data into the innerHTML of an element. I did not want to waste time processing the data on the client side so I simply reworked the creation of cells. For example

                // Need to create the HTML if new, or if a rendering function is defined

                if ( !nTrIn || oCol.mRender || oCol.mData !== i )
                {

                    //nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );

                    nTd.appendChild(document.createTextNode(_fnGetCellData(oSettings, iRow, i, 'display')));

                }

Or this example in sorting:

            if ( /*was just sTitle*//*document.createTextNode(column.sTitle).innerHTML*/column.sTitle != cell.text() ) {

                //cell.html( column.sTitle );
                while (cell[0].lastChild) {

                    cell[0].removeChild(cell[0].lastChild);
                }

                cell[0].appendChild(document.createTextNode(column.sTitle));
            }

Note the replacement is just appending a text node. Some additional code was required to ensure the nodes are empty but it seems to be a relatively simple (and beneficial?) fix.

How can we go about implementing these changes? Is it something that would be possible to do for a nightly build that might work as an initialization option?

Replies

  • allanallan Posts: 63,205Questions: 1Answers: 10,415 Site admin

    Hi,

    You can use the text renderer to escape potentially dangerous HTML with 1.10.10 - for example:

    columns: [
      { data: ..., render: $.fn.dataTable.render.text() },
      ...
    ]
    

    I've just been writing the documentation for exactly this in fact. I might get it published today, but more likely it will be next week.

    As for the change to DataTables core - that is certainly something I will consider - I think its a good idea, but would like to consider the knock-on effects for things like FixedColumns etc which also create their own HTML. For the moment, the renderer is the way to do it.

    Regards,
    Allan

  • matt_matt_ Posts: 3Questions: 1Answers: 0

    Sounds good, I'll use the render solution you've provided for the time being. Thank you!

  • CesarDCesarD Posts: 13Questions: 3Answers: 0

    How can I implement this on a server-side enabled DataTable?
    Could you provide some example on how to configure the columns with this? Thanks!

  • allanallan Posts: 63,205Questions: 1Answers: 10,415 Site admin

    You would take the approach and examples discussed above. See also the security manual page.

    Allan

  • joshuasiegaljoshuasiegal Posts: 19Questions: 2Answers: 1
    edited December 2016

    Hi allan,

    It seems like this approach:

    columns: [
      { data: ..., render: $.fn.dataTable.render.text() },
      ...
    ]
    

    only works for one-dimensional data?

    What I'm looking for is a way to process the cell content before/as it gets written to the page, but even after doing something like this in the column mapping:

    columnMap = [
          {title:'column', class:'class-foo', data:function(row, type, val, meta) {
            if (type=='display') {
              return '<a href="/blah/' + row.id + '">' + row.name + '</a>';
            }
            return row.name;
          }},
    

    and then

    data:columnMap
    

    I just want to be able to take whatever is displayed and filter it, even if it's already been concatenated like this.

    Is this possible?

    The render.text() function is also showing up as 'not a function' in 1.10.9 - is this a new feature in 1.10.10?

    Much thanks as always!

  • allanallan Posts: 63,205Questions: 1Answers: 10,415 Site admin

    Hi,

    If you implement your own rendering function, then you'd need to also perform your own XSS escaping if you want to do it on table output (rather than data input, which is the default with Editor).

    The render.text() function is also showing up as 'not a function' in 1.10.9 - is this a new feature in 1.10.10?

    That is correct. The release notes for 1.10.10 are available here. 1.10.13 is the current release.

    Allan

  • joshuasiegaljoshuasiegal Posts: 19Questions: 2Answers: 1

    Hi, thanks for the response.

    I'm not using Editor, just dataTables with an ajax source that is populating my column maps, as per my snippet above.

    So do I understand that I can use a custom render function (with my own XSS escaping) in addition to columns:columnMap ?

    I haven't been able to get that to work. It seems to see my function but not use it. Does using columns: override using render: ?

    Thanks!

  • allanallan Posts: 63,205Questions: 1Answers: 10,415 Site admin

    So do I understand that I can use a custom render function (with my own XSS escaping) in addition to columns:columnMap ?

    It really depends what columnMap is. You can only have columns once per initialisation, although you can use columnDefs to provide multiple objects for each column. I would suggest that, if possible, you just have a single object for each column that defines everything about that column. That would keep things simpler.

    Allan

  • joshuasiegaljoshuasiegal Posts: 19Questions: 2Answers: 1
    edited December 2016

    Here is what I have:

    dashboardOrdersDTO = $dashboardOrdersTable.DataTable({
              destroy:true,
              data:theData, //massaged / normalized elsewhere
              columns:dashboardOrdersMap,
              render:$.fn.dataTable.render.safehtml(),
              autoWidth:false,
              order:dashboardOrdersDefaultSort, //again, defined elsehwere
              paging:false,
              dom:'ft',
              searching: false,
              language: {
                emptyTable: 'foo', //localized value
                zeroRecords: 'bar' //localized value
              }
            });
    

    and in that plugin:

    jQuery.fn.dataTable.render.safehtml = function() {
      return function(data,type,row) {
        console.log("SAFE HTML");
        if (data && type == 'display') {
          return data
          .replace( /&/g, '&amp;' )
          .replace( /</g, '&lt;' )
          .replace( />/g, '&gt;' );
        }
      }
    };
    

    it doesn't give me "safehtml is not a function" or anything like that. but i can't even get the logging statement - it's like it's not running / calling it.

  • allanallan Posts: 63,205Questions: 1Answers: 10,415 Site admin

    There is no render option at the top level of the DataTables configuration. It is columns.render that you need to assign the function to.

    Allan

  • joshuasiegaljoshuasiegal Posts: 19Questions: 2Answers: 1

    Ah, so in my case, it would go within my mapping. Thank you!

This discussion has been closed.