How to work around setting innerHTML causing escape sequences to expand?

I am trying to avoid a cross-site scripting vulnerability on my server. Before any user-inputted string is embedded within HTML or sent to client-side javascript code it is escaped ('<' replaced with '&lt;', '&' replaced with '&amp;', etc.) When embedding into HTML this works mostly fine; the HTML code produced does not contain any HTML elements inside the user-provided string. However, when the client-side javascript inserts HTML into the document, the escape sequences get expanded back into their special characters, which can result in user-inputted tags appearing in the document HTML. Here's approximately what I'm doing, javascript client-side:

// response_data received from XMLHttpRequest and parsed as JSON
var s = "";
for (var i = 0; i < response_data.length; ++i) {
    s += "<p>";
    s += response_data[i];
    s += "</p>";
}
console.log(s);
elem.innerHTML = s;

Suppose the user inputted the string "abcde <script>alert("Hello!");</script>" earlier. Then response_data could be ["abcde &lt;script&gt;alert(&quot;Hello!&quot;);&lt;/script&gt;"]. The print to console shows s to be "<p>abcde &lt;script&gt;alert(&quot;Hello!&quot;);&lt;/script&gt;</p>". However, when I assign elem.innerHTML, I can see in Inspect Element that the inner HTML of the element is actually <p>abcde <script>alert("Hello!");</script></p>! I don't think it executed, probably because of some browser security features regarding script tags within p tags, but it's obviously not very good. How do I work around this?

Code snippet (run and inspect element over the text created, it shows a script tag within the p tag):

var div_elem = document.querySelector("div");
div_elem.innerHTML = "<p>&lt;script&gt;alert(&quot;Hello!&quot;);&lt;/script&gt;</p>";
<html>
  <head></head>
  <body>
    <div></div>
  </body>
</html>

1 answer

  • answered 2020-07-29 17:23 Jonathan Rosa

    Use innerText, it's like innerHTML but it's treated as pure text and won't decode the HTML entities. Edit:

    Set innerHTML to the p tags, then set the actual text using innerText on the tag

    elem.innerHTML = "<p></p>";
    elem.childNodes[0].innerText = s;