Aniki219
Aniki219

Reputation: 31

How do I protect against XSS/Injection when using jquery.append?

I'm making a javascript code editor for users on my site. One of the features I built was a custom console. Users can write console.log in their code and the logged string gets appended to a div on the page doing something like this:

  function toConsole(str) {
    var myconsole = document.getElementById("console-text");

    var message = document.createElement("span");
    message.append(str);

    myconsole.append(message);
}

str is set to whatever string the user inputs into console.log. Can appending this string run malicious code on my page? (The .append() jQuery api page says 'yes' but I can't seem to get it to interpret anything I write as html)

If so how can I prevent this and how can I test to make sure it's safe?

Upvotes: 3

Views: 12820

Answers (4)

mehdi pourhashemi
mehdi pourhashemi

Reputation: 17

jquery.append() and jquery.html() are not secure against XSS attack Unless you sanitize the data when you want to display the that to the user.

function encodeHTML(s) {
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');}

but jquery.text() is safe because it does not render the html code and brings it in raw.

Upvotes: 0

Louys Patrice Bessette
Louys Patrice Bessette

Reputation: 33933

I will suggest you .parseHTML() with a bemol, as stated in the documentation.

.parseHTML(), as the method name says, parses a string to interpret the HTML.
The third argument keepScripts is defaulted to false... Setting it to true would open the gates wide to scripts.

So it "normally removes" the script tags. If no HTML or text is found at all, it returns undefined (like demo case #3). So you problably will need to add an if condition to avoid appending the text "undefined".

So... In the demo below, I used your posted "script to append" quite as-is... I just added the HTML parsing method.

IMPORTANT, case #1 to #4 are safe... But #5 is a breach. If there is an inline on[event] attribute in the parsed HTML, it will go through the "script filter" and may execute.

$(".console_ok").on("click",function(){
  toConsole( $(this).prev(".console_input").val() );
});

function toConsole(str) {

  str = $.parseHTML(str)[0];

  var myconsole = document.getElementById("console-text");
  var message = document.createElement("span");
  message.append(str);

  myconsole.append(message);
}
input{
  width: 60em;
}
#console-text{
  height:8em;
  width:20em;
  background-color: #bbb;
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

console.log test #1:<input class="console_input" value="Just text">
<button class="console_ok">OK</button><br>
<br>
console.log test #2:<input class="console_input" value="<h1>Some HTML</h1>">
<button class="console_ok">OK</button><br>
<br>
console.log test #3:<input class="console_input" value="<script>alert('A script!!!');</script>">
<button class="console_ok">OK</button><br>
<br>
console.log test #4:<input class="console_input" value="<div style='height:20px;background-color:red;'><script>alert('A script!!!');</script>And some <b>bold</b> text...</div>">
<button class="console_ok">OK</button><br>
<br>
console.log test #5:<input class="console_input" value="<img src='invalid-path' onerror='alert(`JS EXECUTES HERE!!!`);'>">
<button class="console_ok">OK</button><br>
<br>

My console:<br>
<div id="console-text"></div>

   (Please run in ful page mode)
CodePen

You will notice the [0] after $.parseHTML(str)... It's to get the DOM element from the jQuery object, as your function is plain JS. Your function could be written like this too (does the exact same thing):

function toConsole(str) {

  str = $.parseHTML(str);

  var myconsole = $("#console-text");
  var message = $("<span>");
  message.append(str);

  myconsole.append(message);
}

Upvotes: -1

Gutelaunetyp
Gutelaunetyp

Reputation: 1882

As Taplar said, there are different ways. You can also add the HTML to a temporarily-tag and extract the text afterwards. See my third example to do this.

$(function() {
  var stringContainingHtmlAndJavascript = '<div><b>This will be bold</b></div>';

  $('.test1').append(stringContainingHtmlAndJavascript);
  $('.test2').text(stringContainingHtmlAndJavascript);
  $('.test3').text($('<i/>').append(stringContainingHtmlAndJavascript).text());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test1"></div>
<div class="test2"></div>
<div class="test3"></div>

Upvotes: 0

Taplar
Taplar

Reputation: 24965

You can use text() to insert the content as a textNode, which will cause it not to be rendered by the page as markup, but as plain text.

var stringContainingHtmlAndJavascript = '<div><b>This will be bold</b></div>';

$(document.body).text(stringContainingHtmlAndJavascript);
$(document.body).append(stringContainingHtmlAndJavascript);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Upvotes: 1

Related Questions