ParshwaBhavsar/gqlaudit
GitHub: ParshwaBhavsar/gqlaudit
一款纯静态的 GraphQL schema 安全审计工具,通过分析类型系统结构发现配置错误和数据暴露风险,每项发现映射到 OWASP API Top 10。
Stars: 0 | Forks: 0
# gqlaudit




**针对 GraphQL schema 的静态安全审计工具。** 将其指向一个 schema 文件 —— 一个 SDL 定义或保存的 introspection 结果 —— 它就会报告配置错误并提供具体的修复建议,每项发现均映射到 OWASP API Security Top 10。
`gqlaudit` 仅读取文件。它从不联系实际的 endpoint,不发送任何 query,也不执行任何攻击 —— 它分析 schema 结构的方式与 linter 分析源代码的方式相同。
## 目录
- [为什么会有这个项目](#why-this-exists)
- [它检查什么](#what-it-checks)
- [安装](#install)
- [快速开始](#quick-start)
- [用法](#usage)
- [输出示例](#example-output)
- [工作原理](#how-it-works)
- [审计你自己的 API](#auditing-your-own-api)
- [CI 集成](#ci-integration)
- [测试](#testing)
- [项目结构](#project-structure)
- [展示的概念](#concepts-demonstrated)
- [局限性](#limitations)
- [道德与范围](#ethics--scope)
## 为什么会有这个项目
GraphQL 的灵活性也是它的攻击面。一个过度暴露的 field、一个无界限的 list,或者一个没有适当授权的更改角色 mutation,就可以将一个出于好意的 API 变成数据泄露的载体 —— 而传统的 REST 扫描器完全无法发现这些问题,因为它们无法理解 type system。
大多数 GraphQL 安全工具是*攻击性*的:即攻击正在运行的 endpoint 的扫描器。`gqlaudit` 则站在防御的一方。它是开发者或蓝队审查员在部署前,在自己的 **own** schema 上运行的工具,旨在及早发现配置错误 —— 这相当于 API 安全的“左移”。把你的 schema 提供给它,它会告诉你审查员会标记什么问题,以及如何修复每一个问题。
它位于防御性工具集的应用/API 层,是对在文件、日志和网络层工作的检测工具的补充。
## 它检查什么
| 检查项 | 发现的问题 | 严重程度 | OWASP API |
|-------|-------|----------|-----------|
| **敏感 field 暴露** | 名为 `password`、`secret`、`apiKey`、`privateKey`、`token`、`mfaSecret` 等的 Output field | CRITICAL–MEDIUM | API3 |
| **危险的 mutation** | 更改授权的 mutation(角色分配、权限提升、所有权转移、禁用 MFA) | HIGH | API5, API1 |
| **无界限的 list** | 没有分页参数的 List field —— 存在资源耗尽风险 | MEDIUM | API4 |
| **循环关系** | 允许深度嵌套拒绝服务 query 的类型循环 | MEDIUM | API4 |
| **已弃用的 field** | 仍然可查询的已弃用 field —— 通常带有被遗忘的访问控制 | LOW | API9 |
| **PII 暴露** | 暴露个人数据的 field(电子邮件、电话、SSN、DOB、地址、财务信息) | INFO | API3 |
每项发现都包含位置、对风险的纯英语解释、具体的修复指导、OWASP API Top 10 和 CWE 参考以及支持性证据。
## 安装
```
git clone https://github.com/ParshwaBhavsar/gqlaudit.git
cd gqlaudit
pip install -r requirements.txt
pip install -e .
```
需要 Python 3.10+。依赖项:`graphql-core`、`rich`。
## 快速开始
```
# 审计捆绑的故意包含漏洞的示例 schema
python -m gqlaudit audit samples/vulnerable_schema.graphql
# 审计一个干净的 schema(报告零发现)
python -m gqlaudit audit samples/clean_schema.graphql
```
## 用法
```
# 审计一个 SDL schema 文件
python -m gqlaudit audit schema.graphql
# 审计一个已保存的 introspection JSON 结果
python -m gqlaudit audit introspection.json
# 用于自动化的 JSON 输出
python -m gqlaudit audit schema.graphql --format json
# 用于 GitHub code scanning 的 SARIF 输出
python -m gqlaudit audit schema.graphql --format sarif > results.sarif
# CI gate —— 如果存在任何 HIGH 或更严重的发现则以非零状态退出
python -m gqlaudit audit schema.graphql --fail-on high
# 列出所有 checks
python -m gqlaudit list-checks
# 打印标准的 introspection query(用于对您授权的 API 运行)
python -m gqlaudit print-query
```
## 输出示例
针对内置的易受攻击样本(多租户 SaaS schema)运行:
```
GraphQL Security Audit — SDL: samples/vulnerable_schema.graphql
7 object types · 45 fields · 7 mutations
╭─ 🔴 CRITICAL Sensitive field exposed: User.password ───────────────────────╮
│ Location: User.password │
│ Field 'password' on type 'User' has a name suggesting it carries a secret │
│ (returns String). If a resolver populates this field, clients able to query │
│ it can read sensitive data directly. │
│ │
│ Fix: Remove secret-bearing fields from the schema entirely, or ensure the │
│ resolver never returns the raw value. Where a flag is needed, expose a │
│ boolean instead of the value. Enforce field-level authorization. │
│ │
│ OWASP: API3:2023 CWE: CWE-200, CWE-359 │
╰──────────────────────────────────────────────────────────────────────────────╯
… (further findings) …
╭─────── Summary ────────╮
│ Total findings: 28 │
│ 🔴 CRITICAL 3 │
│ 🟠 HIGH 11 │
│ 🟡 MEDIUM 10 │
│ 🔵 LOW 1 │
│ ⚪ INFORMATIONAL 3 │
╰────────────────────────╯
```
相比之下,干净的样本 schema 会报告**零发现** —— 这些检查经过调优,可以避免对遵循良好实践的 schema 产生误报(例如,Relay connection 分页会被识别并避免标记)。
## 工作原理
```
schema file (SDL .graphql | introspection .json)
│
▼
loader.py parse into a graphql-core GraphQLSchema
│ (build_schema for SDL, build_client_schema for JSON)
▼
auditor.py run every registered check over the schema
│
├── sensitive_fields.py secret-bearing field names
├── dangerous_mutations.py auth-state-changing mutations
├── resource_limits.py unbounded lists + type cycles
└── hygiene.py deprecated fields + PII
│
▼
model.py Finding objects (severity, OWASP, CWE, remediation)
│
▼
report.py rich terminal / JSON / SARIF
```
循环关系检查会构建一个关于 object-type 引用的有向图,并运行深度优先循环搜索 —— 这与在 GraphQL 成本分析器中用于检测 query 放大风险的图论方法相同。
## 审计你自己的 API
`gqlaudit` 从不接触网络,因此要审计实际的 API,你首先需要自己(通过授权)导出其 schema,然后审计该文件:
```
# 1. 获取 introspection query
python -m gqlaudit print-query > introspection_query.txt
# 2. 使用您自己的凭据对您自己的 endpoint 运行它
# (使用 curl、Insomnia、Altair 或任何 GraphQL 客户端)
curl -H "Authorization: Bearer $YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d @introspection_query.json \
https://your-api.example.com/graphql > introspection_result.json
# 3. 审计保存的结果
python -m gqlaudit audit introspection_result.json
```
这种分离是故意的:该工具审计的是你有权拥有的 schema,而不是探测 endpoint。
## CI 集成
将 schema 审计添加到你的 pipeline 中,以便在部署前发现回归问题:
```
# .github/workflows/schema-audit.yml
- name: Audit GraphQL schema
run: |
pip install gqlaudit
gqlaudit audit schema.graphql --fail-on high
# 或者上传 SARIF 到 GitHub code scanning:
- name: Audit → SARIF
run: gqlaudit audit schema.graphql --format sarif > gqlaudit.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: gqlaudit.sarif
```
`--fail-on` 接受任何严重级别;如果存在等于或高于该级别的发现,进程将退出并返回状态码 `1`,否则返回 `0`。
## 测试
```
pip install pytest
pytest -q
```
```
35 passed in 0.24s
```
每项检查都有正向测试(它必须捕获的配置错误)和反向测试(它必须放过的格式良好的 schema),外加针对已知误报陷阱(Relay `pageInfo`、connection `edges`)的回归测试,以及针对内置样本 schema 的端到端测试。
## 项目结构
```
gqlaudit/
├── gqlaudit/
│ ├── model.py # Finding, AuditReport, severity, OWASP map
│ ├── loader.py # SDL + introspection JSON loading
│ ├── auditor.py # check registry + orchestration
│ ├── report.py # terminal / JSON / SARIF renderers
│ ├── cli.py # audit / print-query / list-checks
│ └── checks/
│ ├── base.py # BaseCheck + schema traversal helpers
│ ├── sensitive_fields.py # secret-bearing field names
│ ├── dangerous_mutations.py# auth-state-changing mutations
│ ├── resource_limits.py # unbounded lists + circular types
│ └── hygiene.py # deprecated fields + PII
├── samples/
│ ├── vulnerable_schema.graphql # intentionally misconfigured
│ └── clean_schema.graphql # follows best practices → 0 findings
├── tests/
│ └── test_gqlaudit.py # 35 tests
├── requirements.txt
├── pyproject.toml
└── README.md
```
## 展示的概念
- **GraphQL schema 内部原理** —— type、field、argument、mutation、introspection 以及 Relay connection 规范
- **OWASP API Security Top 10 (2023)** —— 每项发现均映射到公认的类别(API1、API3、API4、API5、API9)
- **基于 type system 的静态分析** —— 在不执行任何操作的情况下遍历和推理 schema 结构,与源代码 linter 的模型相同
- **图循环检测** —— 基于 DFS 的循环查找,以识别 query 放大 / DoS 风险
- **SARIF 输出** —— 用于静态分析结果的标准交换格式,支持 GitHub 代码扫描集成
- **误报控制工程** —— 识别合法模式(Relay 分页)以保持干净 schema 的整洁,这也是让 linter 在实践中可用的关键
- **CI 安全门控** —— 基于严重程度阈值的退出代码,用于实现左移强制策略
## 局限性
- **命名启发式方法** —— 敏感 field 和 PII 检测依赖于 field *名称*。一个名字无害的机密 field 不会被捕获;而一个名字吓人但实际无害的 field 可能会被标记。该工具只列出供人工审查的候选项,而不是最终判决。
- **无法查看 resolver** —— schema 不会显示 resolver 是否实际执行了授权。`gqlaudit` 会标记出授权*至关重要*的地方(敏感 field、危险的 mutation),以便审查员验证 resolver —— 但它无法确认 resolver 是否正确。
- **无法看到运行时限制** —— 在服务器 middleware 中强制执行的深度/复杂性限制不会在 schema 中体现,因此循环发现只是结构性风险标记,而不是已确认的漏洞。
这些是静态 schema 分析固有的问题;每项发现上的修复文本都是为了指导人工验证步骤而编写的。
## 道德与范围
专为防御性用途而构建:审计你拥有或被授权审查的 schema、左移 API 安全以及安全教育。`gqlaudit` 读取本地文件,不建立网络连接,也不生成攻击流量。请仅审计你被授权拥有的 schema。
## 许可证
MIT
标签:API安全, GraphQL, JSON输出, Python, 安全规则引擎, 无后门, 逆向工具, 错误基检测, 静态代码分析