XSS via JSON:为什么 Web 应用程序不清理其传入的参数哈希或传出的恶意标签(如脚本)的 JSON 值?

信息安全 Web应用程序 xss 注射 json 导轨
2021-08-25 15:41:50

最近在为一家公司开发基于 Rails 的 Web 应用程序时,我不得不研究 XSS 漏洞。事实证明,在某些地方,应用程序可以采用 HTML 标记(例如,<script>jscodehere</script>直接作为 GET 或 POST 请求中的参数。

然后可以通过应用程序的 params 哈希(所有传入键/值数据可从请求中获得的哈希)访问此参数。

现在,XSS 漏洞源于该站点还以 JSON 版本(例如/cart/1.json)提供其许多页面的数据这一事实。

通过一些我不完全理解的机制(我想这在技术上是“反射型 XSS”?),<script>然后进入 JSON 的未经清理的代码可以通过意外执行来破坏个人机器和其他站点。

我的问题是,为什么这不是一个选择加入系统?Rails 现在是第 4 版,所以我很惊讶仍然必须手动构建解决方案,但这适用于任何 Web 应用程序框架。默认情况下允许参数未经处理是一回事(也许 HTML 标签将用于用户的个人资料页面并且需要格式化)——我认为 Rails 也会对实际的渲染操作进行一些清理,从而限制输出,默认情况下,只有“安全”的 html 标签。

但是,在呈现 JSON 时,它没有进行这样的清理/清理,可能是因为当 JSON 响应被构建和解析时,它过于定制,无法这样做,但我不完全理解为什么

1) 一些内置机制没有到位

2)这没有被更多地讨论——我找不到关于在 web 上的 Rails 或 Sinatra 应用程序的 JSON 渲染中不安全传递 HTML/Script 标签的讨论(我只能找到关于清理 params 哈希的少量信息,因此在输入的过程中对值进行清理,这可以说更好,但可能不适用于所有情况,因为它是一种万能的解决方案;您可能希望保留一些 HTML 标记,但只去除标记<script>,例如)。

3)为什么,至少在 Ruby 世界中,目前只有一个用于清理的库(Sanitize Gem,它实际上只适用于字符串数据类型——你必须编写自己的递归代码来清理哈希,就像参数哈希一样,似乎也没有任何东西写在上面!)。Rails 确实有一个内置的 sanitizer,但它被认为不如这个第三方 Sanitize Gem,并且在对消毒深度(一个字符串)有几个严格级别时不够灵活。

我是否误解了将 JSON 注入作为漏洞的有效性,还是因为 JSON 不是所有 Web 应用程序的核心功能而在很大程度上忽略了这个漏洞?

最终结果:我在后端的主应用程序控制器中使用了一个前置过滤器,以使用 Sanitize 库在每次请求传入时对 params 哈希进行清理。

但是,我相信这会显着减慢应用程序的速度,因为它必须在每个请求上发生,并且 Sanitizer 本质上是在哈希上以递归方式运行一系列正则表达式调用。这样一来,任何标签都不会进入数据库,也永远无法通过 JSON 提取出来,但在性能方面这是一个代价高昂的打击。有没有更好的办法?

2个回答

这经常在任何地方被提及,部分原因是每个人都在不断重复输入卫生就是答案的口头禅。它不是。这是危险的,容易出错的,它需要消失。当然,您应该始终检查您的输入长度是否正确对应于相应的列。

净化输入与净化输出。


清理输入会给人们一种错误的安全感,因为有很多方法可以绕过它,而且因为它很难正确实施,迫使开发人员在谷歌搜索可能安全也可能不安全的过时实施。出于这个原因,最好对output进行清理。

稍后执行此操作的部分原因是因为您希望保留正确的数据,同时还要防止 SQL 注入攻击。SQL 注入攻击在很大程度上被准备好的语句击败,而不是真正的输入卫生。您应该用html 实体替换所有脚本标签和危险输出

这是在输出而不是输入时用 html 实体替换潜在脚本字符的示例:

  1. <变成&lt;,仍然显示<在页面上,但不会弄乱布局或数据库。

  2. >变成&gt;,仍然显示>在页面上,但不会弄乱布局或数据库。


那么为什么不清理输入呢?如果我们正确实施它会怎样?

大多数开发人员都不是 IT 安全专家。大多数开发人员不知道在这个领域该做什么。通过教他们这两种保护数据的常用方法,您可以节省开发时间并显着提高 Web 应用程序的整体安全性。更好的是,您可以帮助您的开发人员理解为什么这是必要的,而不是为什么需要 2340939403424 种不同类型的输入清理,并防止许多以后总会出现的实施问题。

就像我之前说的,在谷歌搜索过时的输入卫生功能并不安全。这是一种虚假的安全感。您需要了解什么是可接受的,什么是不可接受的,以及数据经过的过程。

通过输出卫生,您不必担心稍后会发生一些您忘记的奇怪错误。您不必弄乱可能被错误实现的大量不同复杂功能,并给人一种错误的安全感。您也不必担心脚本被注入。

但是如果我们在插入之前替换所有的 html 实体呢?如果您在客户端尝试这样做,任何人都可以修改请求。如果您在将它们放入数据库之前在后端检查它,那也可以工作......但是有一个问题

如果有一个字段需要放置一个实际的 HTML 实体怎么办?比方说一个包装,一个地址,或者类似的东西。也许它是一个文件名,也许它是你以一种方式实现的东西。


为什么输入卫生很糟糕的例子

也许有人有一个有趣的键盘,在他们的名字或地址中使用了不同的撇号?这可能会使一些数据库对基于 unicode 的走私开放。

也许你需要一个有< or >里面的记录你会怎样做?按 Html 实体搜索?这是非常低效的,并且需要大量的 hack 才能在许多数据库中正常工作。

也许某人的名字中有一个撇号 - 例如,Rory O'Cune使用输入卫生,您正在破坏他的名字并需要更多代码来处理它。如果您的一名员工正在按姓氏进行搜索,但由于姓氏缩写为 而找不到他OCune怎么办?这太可怕了。

这是您使用参数化查询而不是输入卫生的另一个原因。使用准备好的语句和输出卫生,您可以这样做:

SELECT * FROM [table] WHERE [last_name] = @Lastname -- (or ?)

(在 Java@Lastname中是一个?)参数/绑定变量将被正确转换为O'Cune. 不需要有趣的业务。没有可追捕的错误。它更加安全,html entity如果它是一个分页字符,你可以输出它。


那么为什么 Ruby 不能自动修复这个问题呢?

他们为什么要这样做?我还没有遇到任何语言的 JSON 实现会自动删除这些脚本标签。如果您出于某种奇怪的原因想在您的网站上使用 JSON 来显示脚本标签怎么办?

通过删除此功能,您可以防止任何人通过 JSON 输出 HTML,我怀疑这可能会破坏很多人的很多事情。

因此,答案是自己动手在它们被序列化和返回之前,将这些脚本标记替换为 Html 实体。

确保输入安全最好越晚越好。也就是说,当数据输出到页面时。全局“清理”输入数据是不好的,因为在这个阶段无法区分好数据和坏数据(正如你所说,如果你允许用户输入 HTML,框架无法区分应该存在的 HTML 与不存在的 HTML)。即使在输入中使用脚本,有时这也是有效的(想想包含代码的 StackOverflow/StackExchange 文本框)。

然后,您的应用程序应尽可能晚地编码为输出的任何格式。例如,HTML 或 JSON。对于前者,&符号变为&amp;正确呈现以供显示,对于后者,应使用 JSON 编码器,该编码器将转换&\x26JSON 字符串内部。

JSON 本身不是 XSS 风险,因为脚本不会在浏览器中从 JSON 请求执行(JSONP 是另一回事,因为这些包含在脚本 src 引用中,而不是作为数据加载)。JSON 的 XSS 风险是页面上的 JavaScript 尝试使用检索到的 JSON 数据创建或填充 HTML 元素。JavaScript 本身应该对数据进行 HTML 编码或使用安全的对象成员来填充 DOM(例如textContent)。

清理可以作为一个额外的层来完成,但是正确的编码应该是重点。例如,您可能希望验证服务器端的邮政编码或邮政编码仅包含字母数字字符和空格字符。对于更复杂的字段,在不严重限制您的输入能力的情况下,这不是一个选项。

一些框架尝试全局清理数据,例如使用请求验证的 .NET 。然而,漏洞总是被发现,包括这个最近的漏洞。简而言之,它不起作用并导致尝试它的语言出现功能问题。对于从应用程序本身以外的其他来源检索的数据,它也不提供 XSS 保护。