我需要显示通过跨域请求加载的外部资源,并确保只显示“安全”内容。
可以使用 Prototype 的String#stripScripts删除脚本块。但是诸如onclick
or 之onerror
类的处理程序仍然存在。
有没有至少可以的图书馆
- 剥离脚本块,
- 杀死 DOM 处理程序,
- 删除列入黑名单的标签(例如:
embed
或object
)。
那么是否有任何与 JavaScript 相关的链接和示例?
我需要显示通过跨域请求加载的外部资源,并确保只显示“安全”内容。
可以使用 Prototype 的String#stripScripts删除脚本块。但是诸如onclick
or 之onerror
类的处理程序仍然存在。
有没有至少可以的图书馆
embed
或object
)。那么是否有任何与 JavaScript 相关的链接和示例?
2016 年更新:现在有一个基于 Caja sanitizer的Google Closure包。
它有一个更干净的 API,被重写以考虑现代浏览器上可用的 API,并与 Closure Compiler 更好地交互。
无耻的插件:请参阅caja/plugin/html-sanitizer.js以获取经过彻底审查的客户端 html sanitizer。
它被列入白名单,而不是黑名单,但白名单可根据CajaWhitelists进行配置
如果要删除所有标签,请执行以下操作:
var tagBody = '(?:[^"\'>]|"[^"]*"|\'[^\']*\')*';
var tagOrComment = new RegExp(
'<(?:'
// Comment body.
+ '!--(?:(?:-*[^->])*--+|-?)'
// Special "raw text" elements whose content should be elided.
+ '|script\\b' + tagBody + '>[\\s\\S]*?</script\\s*'
+ '|style\\b' + tagBody + '>[\\s\\S]*?</style\\s*'
// Regular name
+ '|/?[a-z]'
+ tagBody
+ ')>',
'gi');
function removeTags(html) {
var oldHtml;
do {
oldHtml = html;
html = html.replace(tagOrComment, '');
} while (html !== oldHtml);
return html.replace(/</g, '<');
}
人们会告诉您,您可以创建一个元素,然后分配innerHTML
然后获取innerText
or textContent
,然后转义其中的实体。不要那样做。它容易受到 XSS 注入的影响,因为即使节点从未附加到 DOM<img src=bogus onerror=alert(1337)>
也会运行onerror
处理程序。
Google Caja HTML sanitizer可以通过将其嵌入到web worker 中来使其成为“web-ready” 。sanitizer 引入的任何全局变量都将包含在 worker 中,并且处理发生在它自己的线程中。
对于不支持 Web Workers 的浏览器,我们可以使用 iframe 作为 sanitizer 工作的单独环境。 Timothy Chien 有一个polyfill可以做到这一点,使用 iframes 来模拟 Web Workers,这样这部分就为我们完成了。
Caja 项目有一个 wiki 页面,介绍如何将 Caja 用作独立的客户端消毒剂:
ant
html-sanitizer-minified.js
或html-css-sanitizer-minified.js
在您的页面中html_sanitize(...)
工作脚本只需要遵循这些说明:
importScripts('html-css-sanitizer-minified.js'); // or 'html-sanitizer-minified.js'
var urlTransformer, nameIdClassTransformer;
// customize if you need to filter URLs and/or ids/names/classes
urlTransformer = nameIdClassTransformer = function(s) { return s; };
// when we receive some HTML
self.onmessage = function(event) {
// sanitize, then send the result back
postMessage(html_sanitize(event.data, urlTransformer, nameIdClassTransformer));
};
(需要更多的代码才能使 simworker 库工作,但这对本次讨论并不重要。)
演示:https : //dl.dropbox.com/u/291406/html-sanitize/demo.html
永远不要相信客户。如果您正在编写服务器应用程序,请假设客户端将始终提交不卫生的恶意数据。这是一个可以让你远离麻烦的经验法则。如果可以,我建议在服务器代码中进行所有验证和卫生,您知道(在合理程度上)不会被摆弄。也许您可以使用服务器端 Web 应用程序作为客户端代码的代理,该代码从第 3 方获取并在将其发送给客户端之前进行卫生处理?
[编辑] 对不起,我误解了这个问题。但是,我坚持我的建议。如果您在将服务器发送给他们之前对其进行消毒,您的用户可能会更安全。
现在所有主要浏览器都支持沙盒 iframe,我认为有一种更简单的方法可以确保安全。如果更熟悉此类安全问题的人可以查看此答案,我会很高兴。
注意:此方法在 IE 9 及更早版本中肯定不起作用。有关支持沙盒的浏览器版本,请参阅此表。(注意:表格似乎说它在 Opera Mini 中不起作用,但我刚刚尝试过,并且有效。)
这个想法是创建一个禁用 JavaScript 的隐藏 iframe,将您不受信任的 HTML 粘贴到其中,并让它解析它。然后你可以遍历 DOM 树并复制出被认为是安全的标签和属性。
此处显示的白名单只是示例。白名单的最佳选择取决于应用程序。如果您需要一个更复杂的策略,而不仅仅是标签和属性的白名单,则可以通过此方法提供,尽管此示例代码不能提供。
var tagWhitelist_ = {
'A': true,
'B': true,
'BODY': true,
'BR': true,
'DIV': true,
'EM': true,
'HR': true,
'I': true,
'IMG': true,
'P': true,
'SPAN': true,
'STRONG': true
};
var attributeWhitelist_ = {
'href': true,
'src': true
};
function sanitizeHtml(input) {
var iframe = document.createElement('iframe');
if (iframe['sandbox'] === undefined) {
alert('Your browser does not support sandboxed iframes. Please upgrade to a modern browser.');
return '';
}
iframe['sandbox'] = 'allow-same-origin';
iframe.style.display = 'none';
document.body.appendChild(iframe); // necessary so the iframe contains a document
iframe.contentDocument.body.innerHTML = input;
function makeSanitizedCopy(node) {
if (node.nodeType == Node.TEXT_NODE) {
var newNode = node.cloneNode(true);
} else if (node.nodeType == Node.ELEMENT_NODE && tagWhitelist_[node.tagName]) {
newNode = iframe.contentDocument.createElement(node.tagName);
for (var i = 0; i < node.attributes.length; i++) {
var attr = node.attributes[i];
if (attributeWhitelist_[attr.name]) {
newNode.setAttribute(attr.name, attr.value);
}
}
for (i = 0; i < node.childNodes.length; i++) {
var subCopy = makeSanitizedCopy(node.childNodes[i]);
newNode.appendChild(subCopy, false);
}
} else {
newNode = document.createDocumentFragment();
}
return newNode;
};
var resultElement = makeSanitizedCopy(iframe.contentDocument.body);
document.body.removeChild(iframe);
return resultElement.innerHTML;
};
安全漏洞:评论者@Explosion 指出href
属性可以包含 JavaScript,例如<a href="javascript:alert('Oops')">
. 应该可以在清理代码中捕获并删除它,但是上面的代码还没有(还)更新来做到这一点。
你可以在这里试一试。
请注意,在此示例中我不允许使用样式属性和标签。如果您允许它们,您可能想要解析 CSS 并确保它对您的目的是安全的。
我已经在几个现代浏览器(Chrome 40、Firefox 36 Beta、IE 11、Android 版 Chrome)和一个旧浏览器(IE 8)上测试了这个,以确保它在执行任何脚本之前保释。我很想知道是否有任何浏览器有问题,或者我忽略了任何边缘情况。
您无法预料到某些浏览器可能会为了逃避列入黑名单而绊倒所有可能的怪异类型的畸形标记,因此不要将其列入黑名单。有许多你可能需要的不仅仅是脚本/嵌入/对象和处理程序,除去更多的结构。
而是尝试将 HTML 解析为层次结构中的元素和属性,然后针对尽可能最小的白名单运行所有元素和属性名称。还要根据白名单检查您允许通过的任何 URL 属性(请记住,还有比 javascript 更危险的协议:)。
如果输入是格式良好的 XHTML,上面的第一部分就容易多了。
与 HTML 清理一样,如果您可以找到任何其他方法来避免这样做,请改为这样做。有很多很多潜在的漏洞。如果这些主要的网络邮件服务在这么多年后仍然在寻找漏洞,是什么让你认为你可以做得更好?