我需要显示通过跨域请求加载的外部资源,并确保仅显示“安全”内容。
可以使用Prototype的String#stripScripts删除脚本块。但是诸如onclick
或的处理程序onerror
仍然存在。
是否有任何图书馆至少可以
- 删除脚本块,
- 杀死DOM处理程序,
- 删除列入黑名单的标签(例如:
embed
或object
)。
那么,那里有JavaScript相关的链接和示例吗?
我需要显示通过跨域请求加载的外部资源,并确保仅显示“安全”内容。
可以使用Prototype的String#stripScripts删除脚本块。但是诸如onclick
或的处理程序onerror
仍然存在。
是否有任何图书馆至少可以
embed
或object
)。那么,那里有JavaScript相关的链接和示例吗?
Answers:
2016年更新:现在有一个基于Caja消毒剂的Google Closure软件包。
它具有更简洁的API,经过重写以考虑到现代浏览器中可用的API,并且可以与Closure Compiler更好地交互。
无耻的插件:请参阅caja / plugin / html-sanitizer.js,以获取经过全面审查的客户端html清理器。
它被列入白名单,而不是列入黑名单,但是白名单可根据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
或textContent
,然后在其中转义实体。不要那样做。它很容易受到XSS注入的影响,因为即使节点从未连接到DOM,它也<img src=bogus onerror=alert(1337)>
将运行onerror
处理程序。
cssparser.js
与html4
对象相邻,但更重要的是,对象)。此外,它污染了全球window
财产。是否有此代码的网络版本?如果没有,您是否看到一种比创建一个单独项目更好的方法来产生和维护它?
可以通过将Google Caja HTML清理器嵌入到Web worker中来使其“支持网络” 。清理程序引入的所有全局变量都将包含在工作进程中,并且处理将在其自己的线程中进行。
对于不支持Web Workers的浏览器,我们可以将iframe用作单独的环境以供消毒程序使用。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
nameIdClassTransformer
每个HTML名称,元素ID和类列表都会被调用;返回null
将删除该属性。通过在src / com / google / caja / lang / html中编辑JSON文件,您还可以自定义将哪些元素和属性列入白名单。
nameIdClassTranformer
上面的功能,例如拒绝所有<script>
标签并接受<b>
和<i>
标签吗?
永远不要信任客户。如果要编写服务器应用程序,请假定客户端将始终提交不卫生的恶意数据。这是一条经验法则,可以使您摆脱麻烦。如果可以的话,我建议您在服务器代码中进行所有验证和清除操作,您知道(在一定程度上)不会被打扰。也许您可以使用服务器端Web应用程序作为客户端代码的代理,该客户端代码是从第三方获取并进行卫生处理之后再发送给客户端本身的?
[编辑]对不起,我误解了这个问题。但是,我坚持我的建议。如果在将服务器发送给用户之前在服务器上进行清理,则用户可能会更安全。
现在,所有主要的浏览器都支持沙盒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;
};
您可以在这里尝试。
请注意,在此示例中,我不允许样式属性和标签。如果允许它们,则可能需要解析CSS并确保它对您的目的是安全的。
我已经在几种现代浏览器(Chrome 40,Firefox 36 Beta,IE 11,Android版Chrome)和一个旧浏览器(IE 8)上对此进行了测试,以确保在执行任何脚本之前先保释。我想知道是否有浏览器遇到问题,或者我忽略了一些极端情况。
iframe.contentDocument.body.innerHTML = input
,将执行其中的任何脚本标签。
sandbox
属性。
您无法预期格式错误的标记的所有可能的怪异类型,某个地方的某些浏览器可能会跳出黑名单,因此请不要将其列入黑名单。有许多你可能需要的不仅仅是脚本/嵌入/对象和处理程序,除去更多的结构。
相反,尝试将HTML解析为层次结构中的元素和属性,然后针对尽可能少的白名单运行所有元素和属性名称。还要对照白名单检查您通过的所有URL属性(请记住,除了javascript:之外,还有更多危险的协议)。
如果输入格式正确的XHTML,则上面的第一部分会容易得多。
与HTML消毒一样,如果可以找到其他避免方法,请改为这样做。有很多潜在的漏洞。如果多年后主要的Webmail服务仍在发现漏洞,那么您认为自己可以做得更好吗?
所以是2016年,我想我们当中许多人npm
现在在代码中使用模块。sanitize-html
似乎npm上是领先的选择,尽管还有其他选择。
该问题的其他答案为如何制定自己的建议提供了很好的信息,但这是一个棘手的问题,经过良好测试的社区解决方案可能是最佳答案。
在命令行上运行此命令以进行安装:
npm install --save sanitize-html
ES5:
var sanitizeHtml = require('sanitize-html');
// ...
var sanitized = sanitizeHtml(htmlInput);
ES6:
import sanitizeHtml from 'sanitize-html';
// ...
let sanitized = sanitizeHtml(htmlInput);
上面建议的Google Caja库太复杂了,无法配置和将其包含在我的Web应用程序项目中(因此在浏览器上运行)。相反,由于我们已经使用了CKEditor组件,因此我要使用的是它内置的HTML清理和白名单功能,该功能更易于配置。因此,您可以在隐藏的iframe中加载CKEditor实例,然后执行以下操作:
CKEDITOR.instances['myCKEInstance'].dataProcessor.toHtml(myHTMLstring)
现在,当然,如果您在项目中不使用CKEditor,这可能有点过头了,因为组件本身大约是半兆字节(最小化),但是如果您有源代码,也许您可以隔离代码来做列入白名单(CKEDITOR.htmlParser
?),并使其更短。
[免责声明:我是作者之一]
为此,我们为此编写了一个“仅网络”(即“需要浏览器”)开源库https://github.com/jitbit/HtmlSanitizer,该库删除了tags/attributes/styles
除“列入白名单”之外的所有库。
用法:
var input = HtmlSanitizer.SanitizeHtml("<script> Alert('xss!'); </scr"+"ipt>");
由于PS PS使用浏览器来解析和处理DOM,因此它的工作速度比“纯JavaScript”解决方案要快得多。如果您对“纯JS”解决方案感兴趣,请尝试https://github.com/punkave/sanitize-html(不隶属于)
我建议您淘汰框架,从长远来看,这会使事情变得非常容易。
cloneNode:克隆节点复制其所有的属性和它们的值,但不会不复制事件侦听器。
https://developer.mozilla.org/en/DOM/Node.cloneNode
尽管我已经使用树行者一段时间了,但以下内容尚未经过测试,它们是JavaScript最被低估的部分之一。这是您可以抓取的节点类型的列表,通常我使用SHOW_ELEMENT或SHOW_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.');
}
}