客户端 HTML 清理有多安全?

11

最近我一直在研究 Pagedown.js,因为使用 markdown 可以代替丑陋的只读文本框,这很吸引人。

然而,我非常谨慎,因为似乎很容易欺骗经过处理的转换器。我看到过一些关于 Angular.js 和它的 html 绑定的讨论,也听说在 Knockout.js 3.0 发布时曾存在 html 绑定不安全的问题。

似乎某些人只需要做一些类似于以下的事情,就可以禁用 Pagedown.js 中的过滤器 -

var safeConverter = new Markdown.Converter();
// safeConverter is open to script injection

safeConverter = Markdown.getSanitizingConverter();
// safeConverter is now safe

// Override the getSanitizingConverter pseudo-code
Markdown.getSanitizingConverter = function () {
    return Markdown.Converter;
};

他们可能会打开一个站点以进行脚本注入。这不是真的吗?

编辑

那么为什么像那样的库会打包一个用于客户端使用的清洁剂呢?当然,它们说不要渲染未经过净化的HTML,但下一行说使用Markdown.Sanitizer..

Angular的清洁剂服务如何不易受攻击,或者这只是一个幌子?


2
这是完全正确的。客户端没有任何安全性可言。 - Pointy
2
看起来更多的回答者回答似乎这个问题是关于客户端验证的。清理是完全不同的事情(实际上它发生在光谱的完全相反的一侧:(客户端)验证发生在用户发送数据之前,(客户端)清理发生在用户从服务器接收数据之后)。清理确实可以使您的应用程序更安全,但只有与其他措施/实践配合使用才能发挥作用。 - gkalpak
这是朝着正确方向迈出的非常重要的一步,绝对应该成为您的应用程序的一部分。然而,它并不能神奇地解决您所有的问题。它是一个有用的工具,应该得到适当的使用和对待。 - gkalpak
4个回答

24

我认为关于“消毒剂”的目的和性质存在一些误解。

消毒剂(例如 Angular 的 ngSanitize)的目的不是防止“坏”数据被发送到服务器端。相反,它是为了保护非恶意用户免受恶意数据的侵害(这些数据可能是由服务器端的安全漏洞导致的(是的,没有完美的设置),也可能来自其他来源(您无法控制的来源)。)

当然,作为客户端功能,可以绕过消毒剂,但是(由于消毒剂在保护用户(而不是服务器)),绕过它只会使绕过者无保护可言(您对此无能为力,也不应该关心 - 这是他们的选择)。

此外,消毒剂还有另一个(潜在更重要的)作用:消毒剂是一种工具,可以帮助开发人员以更易于测试某些类型漏洞(例如 XSS 攻击)的方式组织代码,甚至有助于实际代码审计以查找此类安全漏洞。

在我看来,Angular 文档很好地概括了这个概念:

 

严格的上下文转义(SCE)是 AngularJS 要求在某些上下文中绑定必须产生标记为安全使用该上下文的值的模式。
  [...]
  SCE 协助编写一种(a)默认情况下很安全的代码和(b)使审计安全漏洞(如 XSS、Clickjacking 等)更加容易

    

[...]
  在更现实的例子中,可能会通过绑定来渲染用户评论、博客文章等。(HTML 只是渲染用户控制输入会创建安全漏洞的上下文之一。)

 针对HTML的情况,你可以使用库来在绑定值之前对不安全的HTML进行消毒,无论是在客户端还是在服务器端使用库来进行操作。如何确保每个使用此类绑定的地方都是绑定到已经被你的库消毒或者在服务器端返回为安全的值并在文档中呈现的内容?如何确保你没有意外删除了消毒该值的代码行,或者重命名了一些属性/字段并且忘记了更新绑定到消毒值的代码行?
如果要默认情况下保证安全性,您需要确保除非某些内容明确指出它是安全的,否则任何此类绑定都是禁止的。然后,您可以审计您的代码(简单的grep命令就可以),以确保仅对那些您可以轻松辨认为安全的值进行此操作 - 因为他们是从您的服务器接收的,由您的库消毒过等等。 您可以组织您的代码库来帮助解决这个问题-例如只允许特定目录中的文件这样做。确保该代码公开的内部API不会将任意值标记为安全,这将成为一个更容易处理的任务。

我知道另一个答案得到了更高的投票,但我相信那只是因为它被回答得更快 - 我相信你的答案更好地解释了客户端清洁器的目的以及它们如何保护服务器/用户以及如果它们被绕过会有什么风险。 - PW Kad

8
你无法在客户端进行消毒处理。任何人都可以关闭JavaScript或更改值并将其提交到您的服务器。
服务器需要进行双重检查。在我看来,客户端实际上只是一个可用性功能。因此,他们在输入时知道自己是否正确或错误。
针对编辑的回应:
它们大多数是方便或提供功能。例如不显眼的验证,如果他们输入无效数字,将使我的文本框变红。那很好,我不需要发布、检查,然后更改文本框边框等等...
但当他们发布时,我仍然需要验证他们的数据。我通常将其保存在一个核心库中,该库可以在各种应用程序(Web、Web服务、移动应用程序、厚客户端等)之间共享。
可用性验证因平台而异。但在核心之前,在任何应用程序信任数据之前,它必须在信任障碍内进行检查。没有人可以在服务器上更改我的if后面的值,但任何人都可以在浏览器中更改该值而不触发我的JS。

4
可以在客户端进行数据清洗(这也是个好主意)。数据清洗并不等同于验证。 - gkalpak
1
阿达玛上将,感谢您的回答,但我觉得这是我对消毒剂持有/曾经持有的同样误解。我认为ExpertSystem的答案更好地回答了问题,并提供了更好的参考 - 如果您不同意,请随时让我知道,我们可以讨论! - PW Kad
我同意,几天前我在阅读他的帖子时投了赞成票。这是一篇很棒的帖子。我注意到你使用了“安全”这个词。问题在于,虽然它可以帮助保护最终用户,但不能替代服务器端的净化和安全措施。我仍然建议不要依赖这种方式来防止XSS攻击,因为靠这个库在客户端工作并不安全。但它确实有帮助:)。感谢你的留言@PWKad。 - TheNorthWes
@ExpertSystem 如果 JavaScript 被关闭,您也不能在客户端进行消毒处理。 - user4155172
3
你也不需要这么做。 - gkalpak
@PWKad 我认为你是对的。这主要是一个语义问题,但我看到很多新手开发者“忽略”了整个服务器端组件。称他们为转义器或编码器可能更合适,因为消毒意味着安全。我不会指望Angular.js甚至编码可能是恶意的数据。但对于经过服务器端验证的HTML,我确实看到了好处。 - TheNorthWes

2

Pagedown可以在服务器端和客户端上运行。

对于在客户端上清理HTML,更有意义的是在输出时而不是输入时进行清理。您不会在向服务器发送数据之前进行清理,但您可能会在从服务器接收数据后进行清理。

想象一下在客户端上进行Web服务调用并从第三方服务获取数据。在呈现之前,可以通过客户端上的清理器传递它。用户可以在自己的计算机上禁用清理,但他们只会伤害自己。

它也很有用,不仅仅是出于安全原因,还可以防止用户输入意外修改周围页面的格式。例如在具有实时预览的HTML帖子中键入(如在StackOverflow上)。


-2

绝对不安全。

应该使用客户端清理/验证的原因有几个:

  • 更容易和更快速地告诉非恶意用户他做错了什么
  • 减少非恶意用户与您的服务器通信的次数(在出现错误的情况下)

您验证的所有内容都可以被更改,因为客户端不受您控制。像开发控制台、fiddler、wireshark 这样的工具允许您以基本上任何想要的方式操纵数据。

因此,只有服务器才负责真正的清理/验证。


2
这个问题不是关于验证,而是关于净化。 - gkalpak
@ExpertSystem 请您解释一下这个内容中的区别好吗?验证 - 判断数据是否处于正确的格式(是/否)。清洗 - 将不符合正确格式的数据转换为正确格式。因此,对于两者的问题 - 都不能保证安全,因为两者都可能被篡改。如果唯一的问题只是我的文本中没有“清洗”这个词 - 那很容易就可以解决。 - Salvador Dali
2
不,这不是“措辞”的问题。它是一个完全不同的概念,在完全不同的时间/地点发生。你看过我的回答吗? - gkalpak
1
@ExpertSystem 是的,我已经阅读了它。它比我的答案长得多,但是答案相似——它不安全,只是为了方便和更好的用户体验。他的问题是“是否安全”,所以基本上我可以依靠这个消毒剂并假设它是防弹的。我的答案是——不,你不能,也永远不应该这样做。它可能会被篡改。我不知道我错在哪里。是的,我没有解释消毒剂在角度中的工作原理,但我不需要这样做。 - Salvador Dali
2
我认为你不理解 Sanitizer 的用途。你提到的理由适用于验证,但不适用于消毒。当然,它不能使您的应用程序防弹(没有什么可以做到这一点),但在多个方面确实使其更安全。 - gkalpak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接