GraphQL API 漏洞

GraphQL 漏洞通常是由于实现和设计缺陷而出现的。例如,侦测功能可能保持活动状态,使攻击者能够查询 API 以收集有关其架构的信息。

GraphQL 攻击通常采用恶意请求的形式,使攻击者能够获取数据或执行未经授权的操作。这些攻击可能会产生严重影响,特别是如果用户能够通过操纵查询或执行 CSRF 漏洞来获得管理员权限。易受攻击的 GraphQL API 也可能导致信息泄露问题。

在本节中,我们将介绍如何测试 GraphQL API。如果您不熟悉 GraphQL,请不要担心 - 我们将随时介绍相关细节。我们还提供了一些实验室,以便您可以练习所学知识。

图形QL示例

更多信息

有关 GraphQL 是什么以及它是如何工作的的完整入门知识,请参阅我们的什么是 GraphQL Web Security Academy 页面。

查找图形QL端点

在测试 GraphQL API 之前,您首先需要找到它的端点。由于 GraphQL API 对所有请求使用相同的端点,因此这是一条有价值的信息。

注意

本节介绍如何手动探测 GraphQL 端点。但是,Burp Scanner 可以在扫描过程中自动测试 GraphQL 端点。如果发现任何此类端点,它会引发“找到 GraphQL 端点”问题。

通用查询

如果您发送到任何 GraphQL 端点,它将在其响应中的某处包含字符串。这被称为通用查询,是探测 URL 是否对应于 GraphQL 服务的有用工具。

query{__typename}{"data": {"__typename": "query"}}

查询之所以有效,是因为每个 GraphQL 端点都有一个名为的保留字段,该字段以字符串形式返回查询对象的类型。__typename

常见终结点名称

GraphQL 服务通常使用类似的端点后缀。在测试 GraphQL 端点时,您应该考虑将通用查询发送到以下位置:

  • /graphql
  • /api
  • /api/graphql
  • /graphql/api
  • /graphql/graphql

如果这些常见终结点未返回 GraphQL 响应,您也可以尝试追加到路径。

/v1
注意

GraphQL 服务通常会用“查询不存在”或类似错误来响应任何非 GraphQL 请求。在测试 GraphQL 端点时,您应该牢记这一点。

请求方法

尝试查找 GraphQL 端点的下一步是使用不同的请求方法进行测试。

生产 GraphQL 端点的最佳实践是只接受内容类型为 的 POST 请求,因为这有助于防范 CSRF 漏洞。但是,某些端点可能会接受替代方法,例如 GET 请求或使用内容类型 的 POST 请求。

application/jsonx-www-form-urlencoded

如果无法通过向常用终端节点发送 POST 请求来找到 GraphQL 终端节点,请尝试使用其他 HTTP 方法重新发送通用查询。

初始测试

发现终结点后,可以发送一些测试请求,以了解有关其工作原理的更多信息。如果端点为网站提供支持,请尝试在 Burp 的浏览器中浏览 Web 界面,并使用 HTTP 历史记录来检查发送的查询。

利用未经净化的论点

此时,您可以开始寻找漏洞。测试查询参数是一个很好的起点。

如果 API 使用参数直接访问对象,则可能容易受到访问控制漏洞的攻击。用户可以通过提供与该信息相对应的参数来访问他们不应该拥有的信息。这有时称为不安全的直接对象引用 (IDOR)。

更多信息

例如,下面的查询请求在线商店的产品列表:

  #Example product query

    query {
        products {
            id
            name
            listed
        }
    }

返回的产品列表仅包含列出的产品。

#Example product response

    {
        "data": {
            "products": [
                {
                    "id": 1,
                    "name": "Product 1",
                    "listed": true
                },
                {
                    "id": 2,
                    "name": "Product 2",
                    "listed": true
                },
                {
                    "id": 4,
                    "name": "Product 4",
                    "listed": true
                }
            ]
        }
    }

从这些信息中,我们可以推断出以下内容:

  • 为产品分配一个连续 ID。
  • 列表中缺少产品 ID 3,可能是因为它已被除名。

通过查询缺失产品的 ID,我们可以获取其详细信息,即使它未在商店中列出,并且未由原始产品查询返回。

 #Query to get missing product

    query {
        product(id: 3) {
            id
            name
            listed
        }
    }
   #Missing product response

    {
        "data": {
            "product": {
            "id": 3,
            "name": "Product 3",
            "listed": no
            }
        }
    }

发现架构信息

测试 API 的下一步是将有关基础架构的信息拼凑在一起。

执行此操作的最佳方法是使用内省查询。内省是一个内置的 GraphQL 函数,使您能够查询服务器以获取有关架构的信息。

内省可以帮助您了解如何与 GraphQL API 进行交互。它还可以泄露潜在的敏感数据,例如描述字段。

使用内省

要使用自检来发现架构信息,请查询字段。此字段在所有查询的根类型上可用。__schema

与常规查询一样,您可以指定在运行侦测查询时要返回的响应的字段和结构。例如,您可能希望响应仅包含可用突变的名称。

探索内省

最佳做法是在生产环境中禁用内省,但并不总是遵循此建议。

您可以使用以下简单查询来探测内省。如果启用了自检,响应将返回所有可用查询的名称。

  #Introspection probe request

    {
        "query": "{__schema{queryType{name}}}"
    }
注意

打嗝扫描仪可以在扫描过程中自动测试内省。如果它发现内省已启用,则会报告“已启用 GraphQL 内省”问题。

运行完全内省查询

下一步是针对终结点运行完全侦测查询,以便可以获取有关基础架构的尽可能多的信息。

下面的示例查询返回有关所有查询、突变、订阅、类型和片段的完整详细信息。


    #Full introspection query

    query IntrospectionQuery {
        __schema {
            queryType {
                name
            }
            mutationType {
                name
            }
            subscriptionType {
                name
            }
            types {
             ...FullType
            }
            directives {
                name
                description
                args {
                    ...InputValue
            }
            onOperation  #Often needs to be deleted to run query
            onFragment   #Often needs to be deleted to run query
            onField      #Often needs to be deleted to run query
            }
        }
    }

    fragment FullType on __Type {
        kind
        name
        description
        fields(includeDeprecated: true) {
            name
            description
            args {
                ...InputValue
            }
            type {
                ...TypeRef
            }
            isDeprecated
            deprecationReason
        }
        inputFields {
            ...InputValue
        }
        interfaces {
            ...TypeRef
        }
        enumValues(includeDeprecated: true) {
            name
            description
            isDeprecated
            deprecationReason
        }
        possibleTypes {
            ...TypeRef
        }
    }

    fragment InputValue on __InputValue {
        name
        description
        type {
            ...TypeRef
        }
        defaultValue
    }

    fragment TypeRef on __Type {
        kind
        name
        ofType {
            kind
            name
            ofType {
                kind
                name
                ofType {
                    kind
                    name
                }
            }
        }
    }
注意

如果启用了自检,但上述查询未运行,请尝试从查询结构中删除 、 和指令。许多端点不接受这些指令作为自检查询的一部分,通常可以通过删除它们来获得更成功的内省。

onOperationonFragmentonField

可视化内省结果

对内省查询的响应可能充满信息,但通常很长且难以处理。

您可以使用 GraphQL 可视化工具更轻松地查看架构实体之间的关系。这是一个在线工具,它获取内省查询的结果并生成返回数据的可视化表示形式,包括操作和类型之间的关系。

使用 InQL

作为手动运行内省查询并可视化结果的替代方法,您可以使用 Burp Suite 的 InQL 扩展。

InQL 是一个 Burp Suite 扩展,可帮助您安全地审核 GraphQL API。当您向其传递 URL 时(通过提供实时终结点链接或上传 JSON 文件),它会发出一个内省查询,请求所有查询和更改,并提供结构化视图以方便浏览结果。

建议

即使完全禁用了内省,有时也可以使用建议来收集有关 API 结构的信息。

建议是 Apollo GraphQL 平台的一项功能,服务器可以在其中建议查询修改 错误消息。这些通常用于查询略有错误但仍可识别的情况(对于 例如,)。

There is no entry for 'productInfo'. Did you mean 'productInformation' instead?

您可能会从中收集有用的信息,因为响应有效地泄露了架构的有效部分。

千里眼是一种工具,它使用建议自动恢复全部或部分 GraphQL 模式,即使禁用了内省。这使得从建议响应中拼凑信息所花费的时间大大减少。

您无法直接在 Apollo 中禁用建议。有关解决方法,请参阅此 GitHub 线程

注意

作为扫描的一部分,打嗝扫描仪可以自动测试建议。如果找到活动建议,Burp Scanner 会报告“已启用 GraphQL 建议”问题。

绕过 GraphQL 内省防御

如果无法为要测试的 API 运行内省查询,请尝试在关键字后插入特殊字符。__schema

当开发人员禁用内省时,他们可以使用正则表达式在查询中排除关键字。您应该尝试使用空格、换行符和逗号等字符,因为它们会被 GraphQL 忽略,但不会被有缺陷的正则表达式忽略。__schema

因此,如果开发人员仅排除了 ,则不会排除以下自省查询。

 #Introspection query with newline

    {
        "query": "query{__schema
        {queryType{name}}}"
    }

如果这不起作用,请尝试通过备用请求方法运行探测,因为侦测可能只能通过 POST 禁用。 尝试 GET 请求或内容类型为 .x-www-form-urlencoded

下面的示例显示了通过 GET 发送的自检探测器,其中包含 URL 编码的参数。


    # Introspection probe as GET request

    GET /graphql?query=query%7B__schema%0A%7BqueryType%7Bname%7D%7D%7D
注意

如果端点仅接受通过 GET 的内省查询,并且您希望使用 InQL 扫描程序分析查询结果,则首先需要将查询结果保存到文件中。然后,您可以将此文件加载到 InQL 中,在那里它将正常解析。

使用别名绕过速率限制

通常,GraphQL 对象不能包含多个同名的属性。别名使你能够通过显式命名希望 API 返回的属性来绕过此限制。您可以使用别名在一个请求中返回相同类型对象的多个实例。

虽然别名旨在限制您需要进行的 API 调用次数,但它们也可用于暴力破解 GraphQL 端点。

许多端点将具有某种速率限制器来防止暴力攻击。某些速率限制器根据收到的 HTTP 请求数而不是在终结点上执行的操作数来工作。由于别名有效地使您能够在单个 HTTP 消息中发送多个查询,因此它们可以绕过此限制。

下面的简化示例显示了一系列别名查询,用于检查商店折扣代码是否有效。此操作可能会绕过速率限制,因为它是单个 HTTP 请求,即使它可能用于一次检查大量折扣代码。


    #Request with aliased queries

    query isValidDiscount($code: Int) {
        isvalidDiscount(code:$code){
            valid
        }
        isValidDiscount2:isValidDiscount(code:$code){
            valid
        }
        isValidDiscount3:isValidDiscount(code:$code){
            valid
        }
    }

GraphQL CSRF

跨站点请求伪造 (CSRF) 漏洞使攻击者能够诱使用户执行他们不打算执行的操作。这是通过创建一个恶意网站来完成的,该网站伪造了对易受攻击的应用程序的跨域请求。

更多信息

有关 CSRF 漏洞的更多信息,请参阅 CSRF 学院主题

GraphQL 可用作 CSRF 攻击的载体,攻击者借此创建漏洞,导致受害者的浏览器以受害者用户身份发送恶意查询。

CSRF over GraphQL 漏洞是如何产生的?

当 GraphQL 端点未验证发送给它的请求的内容类型且未实现 CSRF 令牌时,可能会出现 CSRF 漏洞。

只要内容类型经过验证,使用内容类型 的 POST 请求就可以防止伪造。在这种情况下,即使受害者访问恶意站点,攻击者也无法使受害者的浏览器发送此请求。application/json

但是,替代方法(如 GET 或任何内容类型为 ) 的请求可以由浏览器发送,因此如果终结点接受这些请求,则可能会使用户容易受到攻击。在这种情况下,攻击者可能能够利用漏洞向 API 发送恶意请求。

x-www-form-urlencoded

构建 CSRF 攻击和提供漏洞利用的步骤对于基于 GraphQL 的 CSRF 漏洞与“常规”CSRF 漏洞的步骤相同。有关此过程的详细信息,请参阅如何构造 CSRF 攻击

防止 GraphQL 攻击

为了防止许多常见的 GraphQL 攻击,请在将 API 部署到生产环境时执行以下步骤:

  • 如果您的 API 不适合公众使用,请禁用对其的自检。这使得攻击者更难获取有关 API 工作原理的信息,并降低不需要的信息泄露的风险。

    有关如何在 Apollo GraphQL 平台中禁用内省的信息,请参阅此博客文章

  • 如果您的 API 旨在供公众使用,则可能需要启用自检。但是,您应该查看 API 的架构,以确保它不会向公众公开意外字段。

  • 确保已禁用建议。这可以防止攻击者使用千里眼或类似工具来收集有关底层架构的信息。

    您无法直接在 Apollo 中禁用建议。有关解决方法,请参阅此 GitHub 线程

  • 确保您的 API 架构不会公开任何私有用户字段,例如电子邮件地址或用户 ID。

防止 GraphQL 暴力攻击

使用 GraphQL API 时,有时可以绕过标准速率限制。有关此内容的示例,请参阅使用别名绕过速率限制部分。

考虑到这一点,您可以采取一些设计步骤来保护 API 免受暴力攻击。这通常涉及限制 API 接受的查询的复杂性,并减少攻击者执行拒绝服务 (DoS) 攻击的机会。

要防御暴力攻击,请执行以下操作:

  • 限制 API 查询的查询深度。术语“查询深度”是指查询中的嵌套级别数。高度嵌套的查询可能会对性能产生重大影响,如果它们被接受,可能会为 DoS 攻击提供机会。通过限制 API 接受的查询深度,可以降低发生这种情况的可能性。

  • 配置操作限制。操作限制使您能够配置 API 可以接受的唯一字段、别名和根字段的最大数量。

  • 配置查询可以包含的最大字节数。

  • 请考虑在 API 上实施成本分析。成本分析是一个过程,在此过程中,库应用程序在收到查询时识别与运行查询关联的资源成本。如果查询的计算过于复杂而无法运行,API 会删除它。

通过 GraphQL 防止 CSRF

为了专门防御 GraphQL CSRF 漏洞,请在设计 API 时确保以下几点:

  • 您的 API 仅接受通过 JSON 编码的 POST 进行的查询。

  • API 验证提供的内容是否与提供的内容类型匹配。

  • 该 API 具有安全的 CSRF 令牌机制。

参考:portswigger

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值