在客户端清理/重写 HTML

IT技术 javascript html security html-sanitizing
2021-01-25 11:51:14

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

可以使用 Prototype 的String#stripScripts删除脚本块。但是诸如onclickor 之onerror类的处理程序仍然存在。

有没有至少可以的图书馆

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

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

6个回答

2016 年更新:现在有一个基于 Caja sanitizerGoogle 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, '&lt;');
}

人们会告诉您,您可以创建一个元素,然后分配innerHTML然后获取innerTextor textContent,然后转义其中的实体。不要那样做。它容易受到 XSS 注入的影响,因为即使节点从未附加到 DOM<img src=bogus onerror=alert(1337)>也会运行onerror处理程序。

原来已经有一个webbrowsers
2021-03-12 11:51:14
太好了,看起来这里有一些文档:code.google.com/p/google-caja/wiki/JsHtmlSanitizer
2021-03-14 11:51:14
@phihag 该包适用于 nodejs,而不是浏览器。
2021-03-14 11:51:14
@phihag,在google-caja-discuss 上询问,他们可能会指向您打包的。我相信 window 对象污染是为了向后兼容,所以任何新的包版本可能都不需要。
2021-03-30 11:51:14
Caja HTML sanitizer 代码看起来不错,但需要一些胶水代码(相邻的cssparser.js,但更重要的是html4对象)。此外,它会污染全局window属性。是否有此代码的网络版本?如果没有,您是否看到比为其创建一个单独的项目更好的生产和维护方式?
2021-03-31 11:51:14

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.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

@AsGoodAsItGets 正如当前版本中的注释所描述的那样nameIdClassTransformer为每个 HTML 名称、元素 ID 和类列表调用;返回null将删除该属性。通过编辑src/com/google/caja/lang/html 中的 JSON 文件,您还可以自定义列入白名单的元素和属性。
2021-03-21 11:51:14
@AustinWang Web workers 并不是绝对必要的,但由于清理可能在计算上很昂贵并且不需要用户交互,因此它非常适合该任务。(我还在主要答案中提到包含全局变量。)
2021-03-25 11:51:14
我找不到这个库的体面文档。我在哪里/如何指定我的元素和属性白名单?
2021-03-26 11:51:14
@JefferyTo 对不起,也许我太笨了,但我不明白。您引用的 JSON 文件未在上面的示例和演示中使用。我想在浏览器中使用这个库,所以我看了你的演示。你能修改nameIdClassTranformer上面函数,例如拒绝所有<script>标签并接受<b><i>标签吗?
2021-04-02 11:51:14
很好的答案。杰弗里,你能解释一下为什么清理工作需要由网络工作者来完成吗?
2021-04-06 11:51:14

永远不要相信客户。如果您正在编写服务器应用程序,请假设客户端将始终提交不卫生的恶意数据。这是一个可以让你远离麻烦的经验法则。如果可以,我建议在服务器代码中进行所有验证和卫生,您知道(在合理程度上)不会被摆弄。也许您可以使用服务器端 Web 应用程序作为客户端代码的代理,该代码从第 3 方获取并在将其发送给客户端之前进行卫生处理?

[编辑] 对不起,我误解了这个问题。但是,我坚持我的建议。如果您在将服务器发送给他们之前对其进行消毒,您的用户可能会更安全。

实际上,随着 node.js 的流行,javascript 解决方案也可能是服务器端解决方案。至少我是这样结束的。尽管如此,这是一个很好的建议。
2021-04-01 11:51:14

现在所有主要浏览器都支持沙盒 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)上测试了这个,以确保它在执行任何脚本之前保释。我很想知道是否有任何浏览器有问题,或者我忽略了任何边缘情况。

@aldel - 抱歉,我的 VPN 出于某种原因阻止了脚本文件。
2021-03-20 11:51:14
如何以编程方式创建“禁用 JavaScript”的隐藏 iframe?据我所知,这是不可能的。你做的那一刻iframe.contentDocument.body.innerHTML = input,那里的任何脚本标签都将被执行。
2021-03-24 11:51:14
@pwray href 属性可以包含javascript: <a href="javascript: alert('hello')">Test</a>
2021-03-29 11:51:14
这篇文章值得专家们关注,因为它似乎是最明显和最简单的解决方案。它真的安全吗?
2021-03-31 11:51:14
@aldel确实,我不知道。对我们来说,由于缺乏 IE9 的支持,它仍然是行不通的。我想您的解决方案可行,但我认为您应该在回复中澄清您依赖于该sandbox属性。
2021-04-02 11:51:14

您无法预料到某些浏览器可能会为了逃避列入黑名单而绊倒所有可能的怪异类型的畸形标记,因此不要将其列入黑名单。许多你可能需要的不仅仅是脚本/嵌入/对象和处理程序,除去更多的结构。

而是尝试将 HTML 解析为层次结构中的元素和属性,然后针对尽可能最小的白名单运行所有元素和属性名称。还要根据白名单检查您允许通过的任何 URL 属性(请记住,还有比 javascript 更危险的协议:)。

如果输入是格式良好的 XHTML,上面的第一部分就容易多了。

与 HTML 清理一样,如果您可以找到任何其他方法来避免这样做,请改为这样做。有很多很多潜在的漏洞。如果这些主要的网络邮件服务在这么多年后仍然在寻找漏洞,是什么让你认为你可以做得更好?