在客户端清理/重写HTML

我需要显示通过跨域请求加载的外部资源,并确保只显示“ 安全 ”的内容。

可以使用Prototype的String#stripScripts来删除脚本块。 但是像onclick或者onerror这样的处理程序依然存在。

至less有什么图书馆?

  • 剥离脚本块,
  • 杀死DOM处理程序,
  • 删除黑名单标签(例如: embedobject )。

那么是否有任何JavaScript相关的链接和例子呢?

更新2016年:现在有一个基于Caja消毒剂的Google Closure包。

它有一个更清洁的API,被重新编写以考虑到现代浏览器上可用的API,并与Closure编译器更好地交互。


无耻的插件:查看caja /插件/ html-sanitizer.js客户端的HTML卫生洗手,已彻底审查。

它是白名单,而不是黑名单,但白名单可根据CajaWhitelistsconfiguration


如果您想删除所有标签,请执行以下操作:

 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 + '|/?[az]' + tagBody + ')>', 'gi'); function removeTags(html) { var oldHtml; do { oldHtml = html; html = html.replace(tagOrComment, ''); } while (html !== oldHtml); return html.replace(/</g, '&lt;'); } 

人们会告诉你,你可以创build一个元素,并分配innerHTML ,然后获取innerTexttextContent ,然后在那里转义实体。 不要那样做。 由于<img src=bogus onerror=alert(1337)>会运行onerror处理程序,即使节点从未连接到DOM,它也容易受到XSS注入的攻击。

通过将Google Caja HTML清理程序embedded到Web工作人员中,可以使其成为“networking就绪”。 杀菌剂引入的任何全局variables将被包含在工作者中,加工处理在其自己的线程中进行。

对于不支持Web Workers的浏览器,我们可以使用iframe作为单独的环境供消毒工作者使用.Timothy Chien有一个polyfill来完成这个工作,使用iframe来模拟Web Workers,所以部分工作就完成了。

Caja项目有一个关于如何使用Caja作为独立客户端清洁剂的wiki页面:

  • 签出源代码,然后通过运行ant构build
  • 在您的页面中包含html-sanitizer-minified.jshtml-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

永远不要相信客户。 如果您正在编写服务器应用程序,则假定客户端将始终提交不卫生的恶意数据。 这是一个经验法则,可以让你摆脱困境。 如果可以的话,我会build议你在服务器代码中做所有的validation和卫生工作,你知道(在合理的程度上)不会被弄乱。 也许你可以使用服务器端Web应用程序作为你的客户端代码的代理,从第三方获取并在发送给客户端之前做卫生设施?

对不起,我误解了这个问题。 不过,我支持我的build议。 如果您在发送给服务器之前在服务器上进行清理,您的用户可能会更安全。

你不能预料到每一个可能出现的怪异types的格式错误的标记,一些浏览器可能会绊倒黑名单,所以不要黑名单。 除了脚本/embedded/对象和处理程序,您可能需要删除更多的结构。

而是尝试将HTMLparsing为层次结构中的元素和属性,然后针对尽可能最小的白名单运行所有元素和属性名称。 同时检查您通过白名单的任何URL属性(请记住有更多的危险协议比JavaScript :)。

如果input是格式良好的XHTML,则上面的第一部分要容易得多。

和HTML一样,如果你能find其他的方法来避免这样做,那就去做吧。 有许多潜在的漏洞。 如果在这么多年之后,主要的networking邮件服务仍然在发现漏洞,那么你认为自己可以做得更好吗?

所以,现在是2016年,我想现在我们中的很多人都在使用npm模块。 sanitize-html似乎是npm的主要选项,尽pipe还有其他的 。

对这个问题的其他答案提供了如何推出自己的伟大的投入,但这是一个棘手的问题,经过充分testing的社区解决scheme可能是最好的答案。

在命令行上运行以下命令来安装: npm install --save sanitize-html

ES5: var sanitizeHtml = require('sanitize-html'); // ... var sanitized = sanitizeHtml(htmlInput); var sanitizeHtml = require('sanitize-html'); // ... var sanitized = sanitizeHtml(htmlInput);

ES6: import sanitizeHtml from 'sanitize-html'; // ... let sanitized = sanitizeHtml(htmlInput); import sanitizeHtml from 'sanitize-html'; // ... let sanitized = sanitizeHtml(htmlInput);

既然所有主stream浏览器都支持沙箱化的内联框架,那么我认为可以采取更为简单的方式。 如果这个答案可以被更熟悉这种安全问题的人审阅,我很乐意。

注意:这种方法绝对不能在IE 9及更早版本中使用。 请参阅此表以了解支持沙盒的浏览器版本。

这个想法是创build一个JavaScript禁用的隐藏的iframe,粘贴你的不可信的HTML,并让它parsing它。 然后,您可以走DOM树并复制被认为是安全的标签和属性。

这里显示的白名单只是一个例子。 什么是最好的白名单将取决于应用程序。 如果您需要一个更复杂的策略,而不仅仅是标记和属性的白名单,那么可以通过这种方法来适应这个策略,尽pipe不是这个示例代码。

 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; }; 

你可以在这里试试。

请注意,在此示例中,我不允许使用样式属性和标签。 如果你允许他们,你可能会想parsingCSS,并确保它为你的目的是安全的。

我已经在几个现代浏览器(Chrome 40,Firefox 36 Beta,IE 11,Chrome for Android)上testing了这个function,并且在一个老版本(IE 8)上testing了它,以确保在执行任何脚本之前保留它。 我有兴趣知道是否有任何浏览器有问题,或任何我忽略的边缘情况。

 String.prototype.sanitizeHTML=function (white,black) { if (!white) white="b|i|p|br";//allowed tags if (!black) black="script|object|embed";//complete remove tags var e=new RegExp("(<("+black+")[^>]*>.*</\\2>|(?!<[/]?("+white+")(\\s[^<]*>|[/]>|>))<[^<>]*>|(?!<[^<>\\s]+)\\s[^</>]+(?=[/>]))", "gi"); return this.replace(e,""); } 

– 黑名单 – >完整删除标签和内容

– 白名单 – >保留标签

其他标签被移除,但是标签内容被保留

白名单标签的所有属性(剩余的)被删除

上面提到的Google Caja库太复杂了,无法configuration,并且包含在我的Web应用程序项目中(所以在浏览器上运行)。 我所采取的,因为我们已经使用CKEditor组件,是使用它的内置HTML清理和白名单function,这是更容易configuration。 所以,你可以加载一个隐藏的iframe中的CKEditor实例,并执行如下操作:

 CKEDITOR.instances['myCKEInstance'].dataProcessor.toHtml(myHTMLstring) 

现在,如果你没有在你的项目中使用CKEditor,这可能会有些过火,因为组件本身大约有半兆字节(最小化),但是如果你有源代码,也许你可以隔离代码白名单( CKEDITOR.htmlParser ?)并使其更短。

http://docs.ckeditor.com/#!/api

http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor

我build议在你的生活中切割框架,从长远来看,这对你来说过于容易。

cloneNode:克隆节点将复制其所有属性及其值,但不复制事件侦听器

https://developer.mozilla.org/en/DOM/Node.cloneNode

虽然我已经使用treewalkers一段时间了,但下面的内容没有经过testing,它们是JavaScript中价值最低的部分之一。 以下是您可以抓取的节点types列表,通常我使用SHOW_ELEMENTSHOW_TEXT

http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html#Traversal-NodeFilter

 function xhtml_cleaner(id) { var e = document.getElementById(id); var f = document.createDocumentFragment(); f.appendChild(e.cloneNode(true)); var walker = document.createTreeWalker(f,NodeFilter.SHOW_ELEMENT,null,false); while (walker.nextNode()) { var c = walker.currentNode; if (c.hasAttribute('contentEditable')) {c.removeAttribute('contentEditable');} if (c.hasAttribute('style')) {c.removeAttribute('style');} if (c.nodeName.toLowerCase()=='script') {element_del(c);} } alert(new XMLSerializer().serializeToString(f)); return f; } function element_del(element_id) { if (document.getElementById(element_id)) { document.getElementById(element_id).parentNode.removeChild(document.getElementById(element_id)); } else if (element_id) { element_id.parentNode.removeChild(element_id); } else { alert('Error: the object or element \'' + element_id + '\' was not found and therefore could not be deleted.'); } }