Reputation: 31
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
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, '&').replace(/</g, '<').replace(/"/g, '"');}
but jquery.text() is safe because it does not render the html code and brings it in raw.
Upvotes: 0
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
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
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