XSS Prevention From Unsanitized Server Input
XSS Prevention From Unsanitized Server Input
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
Hi,
You can use the
text
renderer to escape potentially dangerous HTML with 1.10.10 - for example: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
Sounds good, I'll use the render solution you've provided for the time being. Thank you!
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!
You would take the approach and examples discussed above. See also the security manual page.
Allan
Hi allan,
It seems like this approach:
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:
and then
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!
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).
That is correct. The release notes for 1.10.10 are available here. 1.10.13 is the current release.
Allan
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!
It really depends what
columnMap
is. You can only havecolumns
once per initialisation, although you can usecolumnDefs
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
Here is what I have:
and in that plugin:
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.
There is no
render
option at the top level of the DataTables configuration. It iscolumns.render
that you need to assign the function to.Allan
Ah, so in my case, it would go within my mapping. Thank you!