【问题标题】:如何防止对 GraphQL/Apollo 服务器的嵌套攻击?
【发布时间】:2016-09-17 04:11:24
【问题描述】:

如何防止针对 Apollo 服务器的嵌套攻击,查询如下:

{
  authors {
    firstName
    posts {
      title
      author {
        firstName
        posts{
          title
          author {
            firstName
            posts {
              title
              [n author]
                [n post]
            }
          }
        }
      }
    }
  }
}

换句话说,如何限制查询中提交的递归数?这可能是一个潜在的服务器漏洞。

【问题讨论】:

    标签: graphql graphql-js apollo


    【解决方案1】:

    截至撰写本文时,GraphQL-JS 或 Apollo Server 中还没有内置功能来解决这个问题,但随着 GraphQL 变得越来越流行,它肯定应该有一个简单的解决方案。这个问题可以通过堆栈的多个级别的几种方法来解决,并且还应该始终与速率限制结合使用,这样人们就不能向您的服务器发送太多查询(这也是 REST 的一个潜在问题)。

    我将仅列出我能想到的所有不同方法,并且由于这些解决方案已在各种 GraphQL 服务器中实现,因此我会尽量使这个答案保持最新。其中有些非常简单,有些则比较复杂。

    1. 查询验证:在每个 GraphQL 服务器中,运行查询的第一步是验证 - 这是服务器尝试确定查询中是否存在任何严重错误的地方,这样我们就可以避免使用实际的服务器资源,如果我们发现前面有一些语法错误或无效参数。 GraphQL-JS 带有a selection of default rules,其格式与 ESLint 非常相似。就像检测infinite cycles in fragments 的规则一样,可以编写验证规则来检测嵌套过多的查询并在验证阶段拒绝它们。
    2. 查询超时:如果无法静态检测到查询会占用过多资源(也许即使是浅查询也可能非常昂贵!),那么我们可以简单地在查询执行中添加超时。这有几个好处:(1) 这是一个不难推理的硬性限制,(2) 这也有助于解决其中一个后端响应时间过长的情况。在许多情况下,您应用的用户宁愿选择缺少的字段,也不愿等待 10 多秒才能得到响应。
    3. 查询白名单:这可能是涉及最多的方法,但您可以提前编制允许查询的列表,并根据该列表检查任何传入查询。如果您的查询是完全静态的(您不会在客户端上使用 Relay 之类的东西生成任何动态查询),那么这是最可靠的方法。您可以在部署应用程序时使用自动化工具从应用程序中提取查询字符串,以便在开发中编写您想要的任何查询,但在生产中只允许您想要的查询。这种方法的另一个好处是您可以完全跳过查询验证,因为您知道所有可能的查询都已经有效。有关静态查询和白名单的更多好处,请阅读这篇文章:https://dev-blog.apollodata.com/5-benefits-of-static-graphql-queries-b7fa90b0b69a
    4. 查询成本限制:(在编辑中添加)类似于查询超时,您可以在查询执行期间为不同的操作分配成本,例如数据库查询,并限制客户端能够执行的总成本每个查询使用。这可以与限制单个查询的最大并行度相结合,这样您就可以防止客户端向您的后端发送启动数千个并行请求的内容。

    (1) 和 (2) 可能是每个 GraphQL 服务器默认应该具备的,特别是因为许多新开发人员可能没有意识到这些问题。 (3) 仅适用于某些类型的应用,但在性能或安全要求非常严格时可能是一个不错的选择。

    【讨论】:

    • 反应很好。你知道任何可以在 GraphQL 中检测循环依赖的工具吗?我还要强调另一个大问题是内存耗尽。 Posts -> Author -> Post 层次结构中的每个更深层次都是一个乘数(即 1 个作者有 5 个帖子 -> 5 个作者有 25 个帖子 -> 25 个作者有 125 个帖子等),它不仅将 SQL/查询复合到底层数据源,但堆分配发回响应。几个级别的深度很容易耗尽几 GB 的 RAM 并使服务器完全崩溃。 1 个查询可以取出 V8!
    • 我认为这是 (2) 和 (3) 会有所帮助的地方。首先,您可以简单地限制单个查询可以执行的对数据库的请求数量(有点像超时)。其次,您可以让您的服务器在生产中只接受预先批准的查询,请参阅此处了解更多详细信息:dev-blog.apollodata.com/…
    • 注意:GraphQL Ruby 内置了用于查询深度和复杂性的分析器。我不确定其他语言的实现。 graphql-ruby.org/queries/analysis.html
    【解决方案2】:

    为了补充 stubailo 回答中的第 (4) 点,这里有一些 Node.js 实现,它们对传入的 GraphQL 文档施加成本和深度界限

    这些是补充验证阶段的自定义规则。

    【讨论】:

      【解决方案3】:

      查询白名单的一个变体是查询签名

      在构建过程中,每个查询都使用与服务器共享但不与客户端捆绑的密钥进行加密签名。然后在运行时服务器可以验证查询是否真实。

      与白名单相比的优势在于,在客户端编写查询不需要对服务器进行任何更改。如果多个客户端访问同一服务器(例如 Web、桌面和移动应用程序),这一点尤其有价值。

      【讨论】:

        【解决方案4】:

        对于查询成本限制,您可以使用graphql-cost-analysis

        这是一个验证规则,它在执行之前解析查询。在您的 GraphQL 服务器中,您只需为您想要的 Schema Type Map 的每个字段分配一个成本配置。

        【讨论】:

          【解决方案5】:

          不要错过graphql-rate-limit ?a GraphQL 指令,用于为您的查询或突变添加基本但精细的速率限制。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-09-12
            • 2021-06-04
            • 2020-01-18
            • 2017-04-15
            • 2011-02-27
            • 2013-08-23
            • 2012-01-26
            • 2017-09-14
            相关资源
            最近更新 更多