| name | conducting-api-security-testing |
| description | 对 REST、GraphQL 和 gRPC API 进行安全测试,识别认证、授权、速率限制、输入验证和业务逻辑中的漏洞。测试人员以 OWASP API 安全 Top 10 作为测试框架,结合 Burp Suite 拦截、Postman 集合和自定义脚本,在每个权限级别测试端点安全性。
|
| domain | cybersecurity |
| subdomain | penetration-testing |
| tags | ["API-security","OWASP-API-Top10","REST","GraphQL","authorization-testing"] |
| version | 1.0.0 |
| author | mahipal |
| license | Apache-2.0 |
执行 API 安全测试
使用场景
- 测试 API 端点的授权缺陷、注入漏洞和业务逻辑绕过
- 评估微服务架构的安全性,其中 API 是主要通信方式
- 验证 API 网关保护(速率限制、认证、输入验证)是否正确执行
- 测试第三方 API 集成的数据暴露和不安全配置
- 评估 GraphQL API 的内省信息泄露、查询复杂度攻击和授权绕过
不适用场景:未经书面授权的 API 测试、未明确划定范围的负载测试或拒绝服务测试,以及未采取安全措施对处理真实金融交易的生产 API 进行测试。
前置条件
- API 文档(OpenAPI/Swagger、GraphQL schema、Postman 集合)或通过应用程序访问来逆向工程 API
- Burp Suite Professional 配置为拦截 API 流量,支持 JSON/XML 内容类型处理
- Postman 或 Insomnia 用于在不同认证上下文中组织和重放 API 请求
- 多个权限级别的有效 API 令牌或凭据(未认证、普通用户、管理员)
- 目标 API 基础 URL 和版本信息
工作流程
步骤一:API 发现和文档
映射完整的 API 攻击面:
步骤二:认证和令牌测试
测试认证机制的弱点:
- JWT 分析:解码 JWT 并检查声明(sub、exp、iss、aud、role)。测试:
- 算法混淆:将
alg 改为 none 并删除签名
- 密钥混淆:将
alg 从 RS256 改为 HS256,用公钥签名
- 弱密钥:使用
hashcat -m 16500 jwt.txt wordlist.txt 暴力破解 HMAC 密钥
- 令牌过期:验证令牌是否过期且过期后不可使用
- 声明篡改:修改 role、userId 或权限声明并重新签名
- OAuth 2.0 测试:检查 redirect_uri 操控、授权码重用、Referer 头中的令牌泄露和缺失 state 参数(CSRF)
- API 密钥安全:测试 API 密钥是否按端点验证、已撤销的密钥是否立即被拒绝,以及查询字符串中的密钥是否出现在访问日志或分析中
步骤三:授权测试(BOLA/BFLA)
测试对象级授权断裂(BOLA)和功能级授权断裂(BFLA):
步骤四:输入验证和注入测试
测试 API 输入的注入和验证缺陷:
- API 参数中的 SQL 注入:用 SQL 注入载荷测试所有参数(路径、查询、请求体、请求头)。JSON API 经常被忽视:
{"username": "admin' OR 1=1--", "password": "test"}
- NoSQL 注入:对于 MongoDB 后端,测试操作符注入:
{"username": {"$gt": ""}, "password": {"$gt": ""}}
- 通过 API 的 SSRF:测试任何接受 URL 的参数(Webhook URL、头像 URL、导入端点),使用内网地址和云元数据端点
- GraphQL 专项注入:测试查询深度攻击、基于别名的批量暴力破解和字段建议枚举
- XML API 中的 XXE:向接受 XML 的 API 端点提交带有外部实体声明的 XML 内容
- 速率限制验证:向认证端点、密码重置和 OTP 验证发送 100+ 次快速请求,测试暴力破解保护
步骤五:数据暴露和响应分析
检查 API 响应中的过度数据暴露:
- 详细响应:比较 API 响应中返回的数据与 UI 显示的内容。API 通常返回超出需要的字段(内部 ID、创建时间戳、其他用户的邮件地址、角色信息)
- 错误消息分析:通过发送畸形输入、无效令牌和不存在的资源来触发错误。检查错误消息是否泄露堆栈跟踪、数据库查询、内部路径或技术细节
- 分页和枚举:测试是否可以通过迭代分页响应(
/api/users?page=1、page=2 等)枚举提取所有记录
- GraphQL 数据暴露:查询非当前用户角色预期的字段。测试跨越关系遍历以访问未授权数据的嵌套查询
- 调试端点:检查
/api/debug、/api/status、/metrics、/health、/.env、/api/swagger.json 是否暴露内部信息
核心概念
| 术语 | 定义 |
|---|
| BOLA | 对象级授权断裂(OWASP API #1);未能验证请求用户是否有权访问特定对象,从而导致 IDOR 攻击 |
| BFLA | 功能级授权断裂(OWASP API #5);未能限制低权限用户访问管理或特权 API 功能 |
| 批量赋值 | API 在未过滤的情况下将客户端提供的数据绑定到内部对象属性,允许攻击者修改不应访问的字段 |
| GraphQL 内省 | GraphQL 内置功能,暴露完整的 API schema,包括所有类型、字段和关系;生产环境应禁用 |
| JWT | JSON Web Token;用于 API 认证的自包含令牌格式,包含用密钥对签名的声明 |
| 速率限制 | 限制客户端在时间窗口内可发出的 API 请求数量的控制,防止暴力破解、枚举和滥用 |
工具与系统
- Burp Suite Professional:HTTP 代理,用于拦截、修改和重放 API 请求,配合 Autorize 等扩展进行自动化授权测试
- Postman:API 开发平台,用于组织端点集合、编写测试脚本和在不同认证上下文中比较响应
- GraphQL Voyager:用于探索通过内省查询获取的 GraphQL schema 的可视化工具
- jwt.io / jwt_tool:用于解码、分析和篡改 JWT 令牌以测试认证绕过的工具
- Nuclei:基于模板的扫描器,具有用于检测常见错误配置和已知漏洞的 API 专项模板
常见场景
场景:金融科技移动应用的 API 安全评估
背景:一家金融科技初创公司的移动银行应用具有 REST API 后端。API 处理账户管理、资金转账、账单支付和交易历史。测试人员拥有 Swagger 文档以及用户和管理员级别的账户。
方法:
- 将 Swagger 规范导入 Postman,在 12 个控制器中生成 87 个端点集合
- 在
/api/v1/accounts/{accountId}/transactions 上发现 BOLA,允许任何已认证用户查看任意账户的交易历史
- 在用户更新端点发现批量赋值,添加
"dailyTransferLimit": 999999 可绕过配置的转账限制
- 识别到资金转账端点缺乏速率限制,允许无限次转账尝试而不受限流
- 发现 JWT 令牌有效期 30 天且无刷新令牌轮换,支持长期会话劫持
- 发现管理端点
/api/v1/admin/users 可使用标准用户令牌访问(BFLA)
- 报告所有发现,附带 CVSS 评分和具体的 API 代码级修复建议
常见陷阱:
- 仅测试 Swagger 中文档化的端点,遗漏未文档化或已弃用的 API 版本
- 未使用每个权限级别的令牌测试同一端点,导致遗漏授权绕过
- 忽略响应体分析中的过度数据暴露,当 UI 仅显示返回字段的子集时
- 仅发送文档中显示的字段,未能测试批量赋值
输出格式
## 发现:交易历史 API 中的对象级授权断裂
**ID**: API-001
**严重性**: 严重(CVSS 9.1)
**受影响端点**: GET /api/v1/accounts/{accountId}/transactions
**OWASP API 类别**: API1:2023 - 对象级授权断裂
**描述**:
交易历史端点在未验证已认证用户是否拥有该账户的情况下返回指定账户的所有交易。
任何已认证用户均可通过替换 accountId 路径参数查看任意账户的完整交易历史。
**概念验证**:
1. 以用户 A 身份认证(账户 ID:ACC-10045)
2. 请求:GET /api/v1/accounts/ACC-10046/transactions
Authorization: Bearer <用户A令牌>
3. 响应:200 OK,包含用户 B 的完整交易历史
**影响**:
任何已认证用户可查看所有 45,000 个客户账户的完整金融交易历史,
包括金额、日期、收款人和交易描述。
**修复建议**:
实施服务端授权检查,在返回数据前验证已认证用户是否拥有所请求的账户:
const account = await Account.findById(accountId);
if (account.userId !== req.user.id) return res.status(403).json({error: "Forbidden"});