API 渗透测试的重点,不是尽可能多地列出接口,而是核对“接口定义、线上路径、参数约束和权限行为”是否一致。只有在授权范围内,将 OpenAPI 文档、实际流量和多版本入口放在同一份接口资产清单中比较,才能区分文档过期、隐藏接口、旧版本暴露与真实未授权访问。
一、先确定授权范围与停止条件
开始测试前,应形成一份可执行的测试授权记录,至少包含:
- 目标范围:域名、网关、服务、API 版本、测试环境或生产环境。
- 允许的方法:GET、HEAD、OPTIONS 等低影响方法是否允许;POST、PUT、PATCH、DELETE 是否需要单独批准。
- 测试账户:匿名访客、普通用户、业务管理员、只读运维账号等。账号应为专用测试账号,不使用真实用户凭证。
- 测试数据:固定的占位对象、测试租户和可回滚资源。
- 访问窗口:测试时间、流量上限、并发限制和联系人。
- 监控与止损:日志、告警、WAF、网关限流和回滚负责人。
- 禁止行为:不得进行凭证喷洒、批量枚举、数据导出、越权修改、删除真实对象或绕过限流造成压力。
如果目标系统没有明确的测试账号、限流策略或回滚方案,应先补齐前置条件,而不是直接扩大请求范围。出现错误率持续升高、业务延迟异常、告警触发或响应内容疑似包含真实敏感数据时,应立即停止并通知系统负责人。

二、建立接口资产清单,而不是只收集 URL
接口收集应同时覆盖“文档声明了什么”和“系统实际上暴露了什么”。建议为每条记录保留以下字段:
| 字段 | 记录内容 |
|---|---|
| 资产来源 | OpenAPI 文件、网关日志、前端代码、移动端包、测试流量或人工发现 |
| 方法与路径 | GET /api/v1/orders/{id} 等 |
| 主机与版本 | 测试环境、生产环境、v1、v2 或无版本路径 |
| 参数 | 路径、查询、请求体、请求头及其类型 |
| 认证要求 | 匿名、Bearer、Cookie、API Key 或未说明 |
| 角色要求 | 普通用户、管理员、服务账号等 |
| 数据对象 | 订单、用户、文件、租户、配置等 |
| 当前状态 | 已验证、待验证、已下线、疑似隐藏接口 |
| 证据位置 | 请求编号、响应摘要、日志时间戳和截图路径 |
1. 从 OpenAPI 2.0/3.0 文件提取接口
OpenAPI 2.0 通常使用 swagger 字段表示版本,OpenAPI 3.x 使用 openapi 字段;两者都通过 paths 描述路径,但认证、安全方案和请求体表达方式存在差异。核验时不要只比较路径字符串,还要比较参数、响应、认证声明和服务器地址。
在离线副本或授权测试环境中,可以先检查定义文件结构:
# 仅读取本地授权副本,不直接探测未知目标
jq -r '
(.paths // {}) | to_entries[] |
.key as $path |
.value | keys[] |
select(. | IN("get","post","put","patch","delete","head","options")) |
"(.|ascii_upcase) ($path)"
' openapi.json | sort -u
对于 OpenAPI 3.x,还应查看 servers;对于 OpenAPI 2.0,则应结合 schemes、host 和 basePath 判断实际基址。不能因为文档中的路径存在,就认为线上一定启用;也不能因为文档未列出,就认定接口不存在。
2. 结合低影响来源补充清单
在授权范围内,可以使用以下来源进行交叉核对:
- API 网关或反向代理访问日志;
- 前端和移动端在测试环境产生的请求;
- 服务端路由注册表或框架路由导出;
- CI/CD 中发布的 OpenAPI 构建产物;
- 版本控制系统中的接口变更记录;
- 经过批准的代理工具流量记录。
不建议把大范围目录扫描作为默认步骤。若确实需要验证少量候选路径,应使用明确的候选列表、低并发和固定的停止条件。例如:
while IFS= read -r path; do
curl --fail-with-body --silent --show-error
--max-time 5 --connect-timeout 3
--request GET "https://api.test.example.invalid${path}"
-H 'Accept: application/json'
-H 'X-Test-Run: inventory-001'
-o "evidence/$(printf '%s' "$path" | sha256sum | cut -c1-12).body"
-w '%{http_code} %{size_download} %{url_effective}n'
sleep 1
done < approved-candidates.txt
这里的域名、候选文件和测试标识都应替换为授权环境内容。不要把响应正文全部写入长期日志,尤其是可能含有个人信息、令牌或业务数据的接口。
三、核对文档、线上路径与版本差异
将来源统一为规范化记录后,可以把文档接口与实际观察到的接口分为三类:
- 文档有、线上无:可能是已下线接口、部署版本不一致、基址错误或文档过期。
- 线上有、文档无:可能是隐藏接口、内部路由、遗留接口或文档发布遗漏。
- 两边都有但定义不同:可能是参数、认证、响应或权限策略发生漂移。
建议使用稳定的规范化格式比较,而不是直接对原始 JSON 做文本差异。至少保留方法、路径、参数名称、必填状态、认证要求和版本标识。
| 差异 | 需要进一步确认的问题 | 初步风险 |
|---|---|---|
v1 与 v2 行为不同 | 是否仍有客户端使用旧版本?安全修复是否同步? | 旧版本暴露 |
| 文档要求认证,实际匿名返回业务数据 | 是否为公开数据?网关是否漏配认证? | 未授权访问 |
| 文档未声明某路径,线上可访问 | 是否为内部接口或临时路由? | 隐藏接口 |
| 参数在文档中必填,服务端可省略 | 是否导致默认租户、默认对象或权限绕过? | 输入校验或授权风险 |
文档列出 403,实际返回 200 空对象 | 是否只是统一响应包装?不同状态是否被客户端混淆? | 需结合数据内容判断 |
文档差异本身不等于漏洞。只有当差异改变了数据可见性、对象访问边界、操作权限或安全控制效果时,才应升级为安全问题。

四、验证认证与对象级访问控制
验证应从低影响、可重复的请求开始。每个测试都要明确“比较对象”和“预期结果”,不要只看 HTTP 状态码。
1. 认证验证
使用专用测试账号建立最小对照组:
- 匿名请求;
- 普通用户请求;
- 具备明确权限的管理账号请求;
- 访问不属于当前用户的测试对象请求。
例如,先对一个专用测试资源执行匿名请求:
curl --silent --show-error --max-time 5
-D evidence/anon.headers
-o evidence/anon.body
"https://api.test.example.invalid/api/v1/test-records/RECORD_A"
-H 'Accept: application/json'
-H 'X-Test-Run: auth-check-001'
再使用测试账号的短期令牌进行对照。令牌不应写入命令历史、截图或报告正文,可以通过安全的环境变量或临时凭据注入:
curl --silent --show-error --max-time 5
-D evidence/user.headers
-o evidence/user.body
"https://api.test.example.invalid/api/v1/test-records/RECORD_A"
-H 'Accept: application/json'
-H "Authorization: Bearer ${TEST_TOKEN}"
-H 'X-Test-Run: auth-check-001'
通常,缺少认证可能返回 401,已认证但无权访问可能返回 403,但项目也可能统一返回 404,或使用 200 搭配业务错误码。因此,判断依据应包括:
- 是否返回了受保护对象的真实字段;
- 响应长度、对象标识和分页总数是否发生异常;
- 是否泄露内部错误、调试信息或用户信息;
- 服务端审计日志是否记录了正确的主体和拒绝原因;
- 不同身份下是否具有一致的访问边界。
2. 对象级访问控制验证
对象级访问控制应使用两个相互独立的测试对象,例如 RECORD_A 属于普通测试用户 A,RECORD_B 属于测试用户 B。使用用户 A 的会话请求 B 的对象,只验证一个或少量固定对象,不进行连续 ID 枚举。
GET /api/v1/test-records/RECORD_B HTTP/1.1
Host: api.test.example.invalid
Accept: application/json
Authorization: Bearer <测试用户A的短期令牌>
X-Test-Run: bola-check-001
需要记录以下结果:
- 返回状态及业务错误码;
- 是否返回 B 的对象内容;
- 是否只返回空壳元数据;
- 是否发生跨租户、跨用户或跨角色访问;
- 请求是否被审计,审计主体是否为用户 A;
- 使用同一对象的管理账号请求时,结果是否符合设计。
如果发现跨对象访问,立即停止扩大验证范围。不要继续尝试批量 ID、修改对象字段或下载关联文件。单个可复核样本通常已经足以支持缺陷确认。
3. 方法与参数边界
对于已批准的修改类接口,优先在专用测试对象上验证字段级权限和状态转换。可以先用不改变业务状态的无效值检查输入校验;只有获得明确授权后,才执行一次可回滚的最小变更。
重点核对:
- 文档声明的必填参数是否真正强制;
- 未声明字段是否会被服务端写入;
- 用户可控的
user_id、tenant_id、role等字段是否被信任; - 查询参数是否改变授权过滤条件;
PATCH与PUT的字段覆盖行为是否一致;- 不同 API 版本是否使用了不同的权限中间件。
五、区分文档过期、隐藏接口与真实风险
判断差异时,可按以下顺序排查:
- 确认环境和基址:检查域名、网关、区域、部署版本和
basePath,避免把测试环境差异误判为漏洞。 - 确认生命周期:查看发布记录、路由注册和下线计划,判断接口是否已废弃但仍被访问。
- 确认认证层位置:检查认证是在网关、服务中间件还是具体控制器执行,避免只根据文档声明下结论。
- 确认数据敏感性:公开目录、健康检查和静态配置不应与用户数据、令牌、财务或管理对象混为一谈。
- 确认可重复性:至少使用同一测试对象重复一次,并由另一名测试人员或服务负责人复核。
- 确认影响范围:只估计已观察到的版本、角色、租户和对象范围,不把理论上可能影响的所有资源直接写成事实。
可以使用这样的判断框架:
| 观察结果 | 结论方向 |
|---|---|
| 文档缺失,但接口只返回公开健康信息 | 文档治理问题,通常不是越权 |
| 文档要求认证,匿名请求返回真实用户对象 | 认证控制缺失,需高优先级处理 |
| 普通用户可读取另一测试用户的对象 | 对象级授权缺陷,需保留最小证据 |
| 旧版本可访问,但与新版本同样执行权限校验 | 版本治理问题,风险取决于数据和生命周期 |
| 旧版本绕过新版本的权限校验 | 版本暴露导致的访问控制缺陷 |
返回 200,但正文只包含统一错误码且无敏感字段 | 可能是响应规范问题,不足以证明未授权访问 |
六、保存可复核但最小化的证据
证据应能让开发、运维和复测人员重现结论,同时避免扩大敏感数据暴露。建议每个发现建立独立目录,保存:
- 测试编号和授权范围;
- 请求时间、时区、主机和 API 版本;
- HTTP 方法、路径和必要请求头;
- 认证主体的匿名化标识;
- 响应状态、关键响应头和经过脱敏的正文;
- 请求与响应哈希;
- 网关或应用日志中的关联 ID;
- 预期结果、实际结果和影响判断;
- 停止条件是否触发以及清理结果。
令牌、Cookie、API Key、个人信息和完整业务响应不应直接放入工单。可以使用字段掩码、哈希或只保留能证明问题的最小 JSON 片段,例如:
{
"object_id": "RECORD_B",
"owner": "user-b-masked",
"accessed_by": "user-a-masked",
"sensitive_fields": "[已脱敏]",
"evidence_hash": "sha256:..."
}
报告复现步骤应使用占位目标和测试对象,并明确“仅限授权环境”。不要把可直接用于真实系统的凭证、批量脚本或未脱敏数据附在报告中。

七、风险判断与整改字段
一个可执行的 API 缺陷报告,至少应包含:
- 标题:指出版本、接口和控制缺陷,例如“
v1订单接口未执行对象级访问控制”。 - 范围:主机、环境、版本、方法和路径。
- 前置条件:需要的测试角色、对象关系和认证状态。
- 复现步骤:使用占位令牌和测试对象的最小请求。
- 预期与实际结果:明确差异,不只写“存在越权”。
- 影响:可访问的数据类型、角色范围、租户边界和是否可修改。
- 证据:请求编号、响应摘要、日志关联 ID 和哈希。
- 原因假设:网关漏配、旧版本中间件缺失、控制器未校验对象归属等。
- 修复建议:服务端强制执行认证、对象归属和字段级授权;统一版本策略;更新 OpenAPI 定义。
- 修复优先级:结合数据敏感性、可利用前置条件、影响范围和暴露时间判断。
- 验证标准:匿名、普通用户、跨对象用户和管理员的预期结果。
- 负责人和截止时间:便于进入整改流程。
修复建议不应只写“增加权限校验”。更具体的要求包括:授权决策必须在服务端完成;对象 ID 不能作为权限凭据;租户边界应从可信会话上下文取得;旧版本应完成下线、隔离或同步安全修复;文档应由发布流程自动生成或进行差异检查。
八、清理、回滚与回归复测
测试结束后,先清理测试过程中创建或修改的资源,再撤销专用令牌、测试账号权限和临时白名单。对于可变更接口,应核对:
- 测试对象是否恢复原状态;
- 临时用户、项目、文件和 webhook 是否删除;
- 网关路由、调试开关和 IP 白名单是否恢复;
- 证据存储是否设置访问控制和保留期限;
- 测试请求是否在监控系统中正确标记;
- 是否需要通知数据保护或运维负责人。
整改后的回归测试应使用与初次发现相同的测试对象关系,并扩大到必要的最小对照组:
| 场景 | 预期结果 |
|---|---|
| 匿名访问受保护接口 | 拒绝,且不泄露受保护数据 |
| 用户 A 访问自己的对象 | 按业务权限允许 |
| 用户 A 访问用户 B 的对象 | 拒绝或返回统一不存在结果 |
| 管理账号访问授权对象 | 按管理权限允许并被审计 |
v1 与 v2 访问同类对象 | 权限边界一致,或旧版本已明确下线 |
| 文档中的必填参数缺失 | 服务端拒绝,不使用危险默认值 |
| 未声明字段出现在请求体 | 被忽略或拒绝,不改变受保护属性 |
最终交付的不应只是一个 URL 列表,而是一份能够解释接口来源、版本关系、参数约束、认证主体、对象权限、证据位置和修复状态的接口资产清单。这样才能把 API 信息收集推进为持续的文档核验、权限验证和回归治理,而不是一次性的路径枚举。

甘肃省 1F
只看接口路径确实容易漏掉权限问题