我想使用标签、属性和值的白名单来清理 html 字符串,然后再将其放入 dom 中。我可以安全地构造一个 dom 元素,并遍历它以实现白名单过滤器,假设在我将 dom 元素附加到文档之前没有恶意 javascript 可以执行吗?这种方法有缺陷吗?
3 回答
根据@rvighne 的回答,在您插入文档之前似乎不会执行任何操作,但至少有这些(不寻常的)异常(在 FF 27.0 中测试):
var userInput = '<a href="http://example.com" onclick="alert(\'boo!\')">Link<\/a>';
var el = document.createElement('div');
el.innerHTML = userInput;
el.addEventListener("click", function(e) {
if (e.target.nodeName.toLowerCase() === 'a') {
alert("I will also cause side effects; I shouldn't run on the wrong link!");
}
});
el.getElementsByTagName('a')[0].click(); // Alerts "boo!" and "I will also cause side effects; I shouldn't run on the wrong link!"
...或者...
var userInput = '<a href="http://example.com" onclick="alert(\'boo!\')">Link<\/a>';
var el = document.createElement('div');
el.innerHTML = userInput;
el.addEventListener("cat", function(e) { this.getElementsByTagName('a')[0].click(); });
var event = new CustomEvent("cat", {"detail":{}});
el.dispatchEvent(event); // Alerts "boo!"
...或...(虽然 setUserData 已被弃用,但它仍在工作):
var userInput = '<a href="http://example.com" onclick="alert(\'boo!\')">Link<\/a>';
var span = document.createElement('span');
span.innerHTML = userInput;
span.setUserData('key', 10, {handle: function (n1, n2, n3, src) {
src.getElementsByTagName('a')[0].click();
}});
var div = document.createElement('div');
div.appendChild(span);
span.cloneNode(); // Alerts "Boo!"
var imprt = document.importNode(span, true); // Alerts "Boo!"
var adopt = document.adoptNode(span, true); // Alerts "Boo!"
...或在迭代期间...
var userInput = '<a href="http://example.com" onclick="alert(\'Boo!\');">Link</a>';
var span = document.createElement('span');
span.innerHTML = userInput;
var treeWalker = document.createTreeWalker(
span,
NodeFilter.SHOW_ELEMENT,
{ acceptNode: function(node) { node.click(); } },
false
);
var nodeList = [];
while(treeWalker.nextNode()) nodeList.push(treeWalker.currentNode); // Alerts 'Boo!'
但是如果没有这种(不寻常的)事件交互,就我能够检测到的而言,单独构建到 DOM 中的事实不会导致任何副作用(当然,上面的示例是人为的,一个不会如果有的话,希望经常遇到它们!)。
嵌入在 HTML 中的脚本在放入文档之前无法执行。尝试在任何页面上运行此代码:
var html = "<script>document.body.innerHTML = '';</script>";
var div = document.createElement('div');
div.innerHTML = html;
你会注意到没有任何变化。如果运行了 HTML 中的“恶意”脚本,那么文档应该已经消失了。因此,您可以使用 DOM 来清理 HTML,而不必担心 HTML 中存在错误的 JS。当然,只要你在你的消毒剂中剪掉脚本。
顺便说一句,您的方法比大多数人尝试的方法非常安全和聪明(用正则表达式解析它,可怜的傻瓜)。但是,最好依靠良好的、受信任的 HTML 清理库,例如HTML Purifier。或者,如果你想在客户端做,你可以使用ESAPI-JS(由@Brett Zamir 推荐)
您可以使用不会执行任何操作的“沙盒”iframe。
var iframe = document.createElement('iframe');
iframe['sandbox'] = 'allow-same-origin';
来自 w3schools:
沙盒属性为 iframe 中的内容启用了一组额外的限制。当沙盒属性存在时,它将:
- 阻止表单提交
- 阻止脚本执行
- 禁用 API
- ...
PS 顺便说一句,这正是我们在 Html Sanitizer 中的操作方式https://github.com/jitbit/HtmlSanitizer - 我们使用浏览器来解释 HTML 并将其转换为 DOM。随意检查代码(或实际使用组件)
(免责声明:我是那个 OSS 项目的贡献者)