This is a kind of an ancient post, but some comments were asking if there was any defense.
Here's an excerpt from the paper:
At this point the string is still harmless and cannoot be used to
execute an XSS attack. However, as soon as this string is inserted
into the browser's DOM by using the innerHTML property, the browser
will mutate the string. This mutation is highly unpredictable since
it is not part of the specified innerHTML handling, but is a
proprietary optimization of HTML code implemented differently in each
of the main browser families. The mutated string now contains a valid
XSS vector, and the attack will be executed on rendering the new DOM
element. Both Server and client filters were unable to detect this
attack because the string scanned in these filters did not contain any
executable code.
This exploit is actually a BROWSER-BASED exploit. And in the world of rather dynamic frameworks like angularjs and jquery, there's quite a bit of innerHTML going on under the hood, even in 2012-2013 when this attack was first described.
The author goes on to say:
If innerHTML is only used to insert trusted code from the
webapplication itself into the DOM, it is not vulnerable.
So the answer to "is there anything we can do?" is: Don't use innerHTML in combination with user input.
But wait... what if my application has thousands of calls to innerHTML or we use jquery? The paper discusses a script they offered called "TrueHTML." When looking for example code, the best I was able to find was here, where you override the client's use of innerHTML, bypassing the functions discussed in the original attack... and without needing to do more than to add a single script file. The paper also discusses a minimal performance hit.
(I'm not 100% sure if the code listing I used ought to be trusted--I didn't write it, copy-pasta at your own risk! That said, it appears to use the technique described in the paper.) I'm not sure if the "isExploitable" check returns a proper value.
Here's an excerpt from the provided github commit:
mitigateMXSS: function (element) {
+ var that = this;
+ if (typeof element.innerHTML === 'string') {
+ Object.defineProperty(element, 'innerHTML', {
+ get: function () { return that.changeInnerHtmlHandler(this, 'innerHTML') },
+ set: function (html) {
+ while (this.firstChild) {
+ this.removeChild(this.lastChild);
+ }
+ this.insertAdjacentHTML('afterBegin', html);
+ }
+ });
+ }
+ },