Web API 渗透测试的价值,不在于证明“某个请求能够返回数据”,而在于证明该请求违反了明确的身份、对象或功能权限规则,并且结果能够由开发、运维和复测人员独立复核。本文仅适用于获得书面授权的测试环境,使用占位域名、测试账号和脱敏数据,重点介绍如何以最小化请求验证对象级授权、功能级授权、认证状态和敏感数据暴露问题。

一、先确认授权边界和停止条件
测试开始前,应将授权内容整理成可执行的测试规则,而不是只保留一封笼统的“允许测试”邮件。至少确认以下事项:
- 目标范围:域名、IP、API 网关、环境名称、版本或租户范围。
- 测试时间窗:开始和结束时间,是否避开批处理、发布、结算等高风险时段。
- 允许的方法:是否允许身份与权限测试、错误配置检查、限速观察,以及是否禁止上传、删除、支付和管理操作。
- 测试账号:普通用户、受限用户、管理员或租户管理员等角色,账号应为专门创建的测试账号。
- 数据限制:只能访问授权测试数据;发现真实个人信息、令牌、密钥或生产数据时应立即停止扩大验证。
- 影响阈值:请求速率、并发数、单次返回量、失败次数和资源消耗上限。
- 停止条件:出现服务异常、数据写入、跨租户数据、敏感凭据、告警升级或无法判断影响时,暂停请求并联系授权方。
- 清理方式:测试产生的数据、会话、临时对象和配置变更由谁清理,如何确认清理完成。
OWASP Web Security Testing Guide 将 API 侦察视为独立测试环节,强调先了解目标和 API 结构,再开展后续验证。可以将 OWASP WSTG 的 API 侦察章节 作为方法参考,但具体测试仍应服从项目授权范围。
二、建立资产和接口清单
API 测试的第一个实际产物应是接口清单,而不是扫描器报告。清单用于回答“测试了什么”和“还有什么没有测试”。
可以从以下资料来源建立清单:
- OpenAPI、Swagger、GraphQL Schema 或内部接口文档;
- 前端 JavaScript、移动端测试包和业务流程中的请求记录;
- API 网关路由、反向代理配置和服务注册信息;
- 测试环境访问日志、调用方清单和版本发布记录;
- 授权范围内的被动流量观察结果。
建议为每个接口记录:
| 字段 | 示例 |
|---|---|
| 接口标识 | GET /api/v1/orders/{order_id} |
| 环境 | staging-api.example.test |
| 认证方式 | Bearer 测试令牌 |
| 预期角色 | 普通用户 |
| 对象范围 | 当前用户所属租户 |
| 操作类型 | 读取 |
| 数据敏感度 | 订单基础信息 |
| 是否产生副作用 | 否 |
| 测试状态 | 未开始、通过、异常、需复核 |
| 证据位置 | 请求编号、响应摘要、日志编号 |
清单中的目标应使用占位域名,例如 https://api.example.test,不要在报告或示例中泄露真实生产域名、内部主机名和令牌。对于文档中存在但实际不可用的接口,应记录“未验证”及原因,不能直接标记为安全。
三、先写出身份与权限假设
越权验证的核心不是随意替换参数,而是先定义服务端应该执行的授权规则。没有预期规则,就无法判断响应差异究竟是漏洞、业务设计还是测试误判。
1. 对象级授权假设
对象级授权关注“当前身份能否访问这个具体对象”。例如:
- 用户甲只能读取自己创建的订单;
- 租户甲的成员不能读取租户乙的项目;
- 普通用户只能修改自己名下的地址;
- 归档对象只能由具备特定职责的角色查看。
测试前准备两个同权限、不同对象范围的账号,例如 user_a 和 user_b,并在测试环境中准备各自独立的对象:
order_a属于user_a;order_b属于user_b;- 两个对象状态和字段结构尽量相似。
验证目标不是“把 ID 改大或改小”,而是确认服务端是否根据当前会话重新判断对象归属。对象标识可以是数字、UUID、路径片段、查询参数或请求体字段,均不应被客户端输入自动视为授权依据。
2. 功能级授权假设
功能级授权关注“当前角色能否执行这个动作”。例如:
- 普通用户可以查看自己的订单,但不能导出全租户订单;
- 项目成员可以读取项目,但不能修改成员权限;
- 运维角色可以查看运行状态,但不能变更业务数据;
- 管理功能不能仅凭隐藏按钮或前端路由限制。
功能级测试应覆盖同一接口在不同角色下的行为,并区分:
- 未登录;
- 已登录的普通用户;
- 已登录的受限角色;
- 具备管理权限的测试账号。
不要仅以 HTTP 状态码判断结果。200、201、204、403、404 和 401 都需要结合响应体、业务状态、审计日志和服务端实际变化解释。
四、采用最小化请求进行验证
低影响验证的原则是:一次只验证一个假设,优先使用只读接口,控制请求频率,不主动扩大数据量。
1. 建立基准请求
先使用账号 A 对其自身对象发起正常请求,保存以下信息:
- 请求方法和路径;
- 脱敏后的请求头;
- 必要的查询参数或请求体;
- 状态码、响应长度和关键字段;
- 请求时间、响应时间和追踪 ID。
示例使用占位目标和环境变量,不包含真实令牌:
export API_BASE="https://api.example.test"
export TOKEN_A="REDACTED_TEST_TOKEN_A"
curl --silent --show-error
--request GET "$API_BASE/api/v1/orders/order_a"
--header "Authorization: Bearer $TOKEN_A"
--header "Accept: application/json"
该请求的作用是建立“合法基线”,不是扫描接口。若基线请求本身失败,应先确认账号状态、接口版本、测试数据和环境配置,不要直接把失败结果解释为访问控制有效。
2. 进行单变量对照
在保持方法、请求头和其他参数不变的情况下,仅将对象替换为 order_b,并使用账号 B 做同样请求。对照表可以这样记录:
| 对照项 | 账号 A 访问 order_a | 账号 A 访问 order_b |
|---|---|---|
| 预期结果 | 允许 | 拒绝或返回统一不存在 |
| 实际状态码 | 200 | 待记录 |
| 响应是否含对象字段 | 是 | 待记录 |
| 服务端数据是否变化 | 否 | 待记录 |
| 是否产生审计日志 | 是 | 待记录 |
如果账号 A 能读取或修改 order_b,应先确认 order_b 是否确实属于不同用户或租户,是否存在共享、委托、团队协作或客服代办等业务规则。只有在业务规则明确禁止访问时,才能将其作为对象级授权问题提交。
3. 处理响应差异
有价值的证据不只是“返回 200”。应比较:
- 状态码;
- 响应正文中的对象标识、用户标识、租户标识;
- 返回字段数量和敏感字段;
- 分页总数、错误码和错误信息;
- 响应时间是否出现异常差异;
- 服务端审计日志中的主体、对象和决策结果。
对于未授权对象,服务端可能返回 403,也可能为避免对象枚举而返回统一的 404。这两种设计都可能合理,关键是未授权请求不能泄露对象内容,也不能产生未授权的状态变化。
五、重点检查认证状态和敏感数据暴露
认证状态
至少验证以下状态:
- 完全不带认证头;
- 使用格式错误或过期的测试令牌;
- 使用账号 A 的令牌访问账号 B 的对象;
- 令牌被撤销或退出登录后再次访问;
- 不同 API 版本或网关路径是否使用一致的认证策略。
应检查认证是否仅由前端控制、是否存在某个版本路径遗漏认证、是否将用户标识从请求参数直接作为可信身份。测试中不要尝试猜测真实令牌,也不要收集非测试用户的会话信息。
敏感数据暴露
即使对象级授权正确,也可能存在对象属性级问题。检查响应是否返回业务不需要的字段,例如:
- 密码哈希、访问令牌、刷新令牌;
- 内部服务地址、调试堆栈和数据库标识;
- 身份证件、支付信息或完整联系方式;
- 未脱敏的日志、密钥或第三方凭据;
- 不同角色不应看到的管理字段。
验证时只需确认字段是否出现、是否可由低权限账号获取,不应下载大量记录或进一步使用暴露的凭据。发现真实敏感数据后,应停止扩大读取范围,保留最小必要证据,并按约定通知授权方。

六、控制请求影响和测试节奏
API 测试不应把并发压测、批量枚举和破坏性操作混入普通权限验证。建议采取以下控制:
- 优先使用
GET、HEAD或明确无副作用的接口; - 每次只发送一个或少量对照请求,并设置合理间隔;
- 不使用大范围 ID 枚举来证明问题;
- 不测试删除、支付、发邮件、改密和批量导出等动作,除非授权明确允许;
- 需要验证写操作时,使用专门测试对象,并提前确认回滚方法;
- 遇到速率限制、错误率升高、队列堆积或响应时间明显上升时立即暂停;
- 记录测试工具的并发、超时、重试和代理配置,避免工具默认行为扩大影响。
如果需要确认写操作是否受权限控制,应优先在隔离环境中使用无业务价值的测试对象,并将请求体中的内容限制为无害标记。例如,先验证服务端是否返回拒绝,再由授权方通过日志或数据库审计确认没有发生变更。
七、形成可复核的漏洞证据
一条合格的发现应让不了解现场的开发人员能够复现和判断。报告至少包含以下字段:
- 标题:例如“普通用户可读取其他租户订单对象”;
- 资产与接口:环境、方法、路径和版本;
- 前置条件:测试账号角色、对象归属和必要配置;
- 预期授权规则:当前身份本应能做什么;
- 复现步骤:控制在最少请求数,使用脱敏参数;
- 实际结果:状态码、关键响应字段和是否产生状态变化;
- 对照结果:合法账号、拒绝场景或修复前后的差异;
- 影响范围:可能暴露的数据类型、受影响角色和租户边界;
- 证据:请求编号、响应摘要、网关日志或审计日志编号;
- 风险判断:机密性、完整性、可用性和业务影响;
- 修复建议:服务端授权位置、字段控制和测试要求;
- 清理结果:创建对象、会话、配置和临时数据是否已清除。
请求示例应脱敏:
GET /api/v1/orders/order_b HTTP/1.1
Host: api.example.test
Authorization: Bearer <TOKEN_A_REDACTED>
Accept: application/json
X-Test-Case: BOLA-001
响应可以只保留用于证明结论的字段:
{
"status": "success",
"order_id": "order_b",
"tenant_id": "tenant_b",
"customer_name": "<REDACTED>"
}
不要在报告中保留完整令牌、Cookie、个人信息、支付数据或可直接使用的密钥。截图也应进行遮罩,并说明截图时间、账号角色和环境。
八、风险判断不能只看状态码
越权问题的严重程度取决于对象和动作,而不是单一的 200 或 403。可以从以下维度判断:
- 是否跨用户、跨团队或跨租户;
- 读取的是普通业务信息还是身份、财务、凭据等敏感数据;
- 仅能读取,还是能够新增、修改、删除或授权;
- 是否需要已认证账号,还是完全未认证即可访问;
- 是否存在稳定、可重复的权限绕过;
- 是否可能影响大量对象,但不应通过批量请求进行实际验证;
- 是否已有审计日志和告警,能否追溯异常访问。
风险描述应区分“已证明的影响”和“合理推断的影响”。例如,单个测试对象被读取可以证明对象级授权失效;但不能据此断言整个数据库已被读取,除非有授权范围内的独立证据。
九、修复建议应落到服务端控制点
常见修复方向包括:
- 在服务端根据认证主体重新加载对象,并执行租户、用户和角色授权检查;
- 将授权逻辑放在业务服务或统一策略层,而不是只依赖前端隐藏按钮;
- 对读取、创建、更新、删除和导出分别定义权限;
- 使用字段白名单和角色化响应模型,避免直接序列化内部对象;
- 对管理接口、调试接口和旧版本 API 采用一致的认证与授权策略;
- 对失败访问记录主体、对象、动作、来源和结果,同时避免日志泄露令牌;
- 对高敏感操作增加重新认证、审批或二次确认;
- 根据业务需要设置分页上限、速率限制和资源消耗控制。
随机化对象标识只能降低枚举便利性,不能替代服务端授权。隐藏路径、修改前端路由或依赖网关规则,也不能替代每个业务接口自身的权限判断。
十、整改复测要证明“问题已关闭”
复测应复用原始测试条件,并保留修复前后的对照:
- 使用原账号 A 访问原对象
order_b; - 确认服务端拒绝访问或返回统一的无对象结果;
- 使用合法账号访问自身对象,确认正常业务没有被误伤;
- 检查读取、更新、删除、导出等相关动作是否同时修复;
- 检查不同 API 版本、网关路径和缓存层是否仍有差异;
- 检查响应中是否仍暴露不必要字段;
- 通过审计日志确认拒绝决策和主体信息正确;
- 清除复测产生的测试对象、令牌和临时配置。
复测结论可分为:
- 已修复:越权请求被拒绝,合法请求正常,日志和字段控制符合预期;
- 部分修复:某个方法或版本已修复,但仍有相关入口可访问;
- 未修复:原始条件下仍可重复;
- 无法验证:环境、账号、数据或日志条件不足,应明确列出缺失项。
最终,Web API 渗透测试应形成一条完整证据链:授权范围定义了可以测试什么,接口清单说明测试覆盖了什么,权限假设说明为什么该访问应被允许或拒绝,对照请求证明实际差异,日志和响应确认影响,整改复测则证明修复没有只停留在前端或单一路径。以这条证据链为主线,越权验证才会真实、可复核,也更值得开发和运维团队投入修复资源。

上海市 1F
先把授权边界写清楚,确实省很多麻烦
广东省深圳市 2F
越权测试最怕把业务规则和漏洞混为一谈
甘肃省 3F
只读接口优先,这个测试节奏比较稳妥
广东省深圳市 4F
复测时能复用原始条件就更有说服力
广东省深圳市 5F
想问下多租户场景通常怎么准备测试数据
重庆市 6F
状态码相同也不能说明权限判断真的一致
甘肃省 B1
@ 琴瑟在御 对,响应体、审计日志和实际变化都得一起看。
日本 7F
对象权限过了,字段白名单也不能省
湖南省张家界市 B1
@ 蓝莓酱 没错,能访问对象不等于能看全字段,敏感字段还得靠服务端白名单单独卡住。
韩国 8F
测试前先写清停止条件,真出异常时能少走很多弯路
重庆市 9F
脱敏证据这点很关键,报告不能留下新风险
日本 10F
复测时别忘了把旧版本接口也过一遍
宁夏银川市 B1
@ 吴倩 这个提醒很关键,旧版本接口也要纳入清单,确认是否仍可访问以及权限控制是否一致。
重庆市 11F
前端隐藏按钮确实不能代替服务端鉴权
重庆市 B1
@ 嚣张獠牙 尤其是管理接口,前端限制撤掉后就不能算控制了。
甘肃省 12F
接口清单比单纯丢一份扫描报告实用
广东省深圳市 B1
@ 四面楚歌 接口清单还能标出未验证项,后续复测不容易漏。
甘肃省 13F
写操作测试还是得提前确认回滚方案
北京市 14F
元宝,别被状态码带偏了,日志和实际数据变化才是硬证据
北美地区 B1
@ 考拉梦 对,状态码只是表象,得结合日志和实际数据变化一起看,不然容易误判。
甘肃省 15F
撤销令牌后的访问状态也值得单独留证。
广东省深圳市 16F
跨版本接口保持一致,常常比想象中难。
重庆市 17F
字段级权限很容易被对象级权限遮住。
重庆市 18F
多租户数据最好让对象归属足够清晰。
上海市南汇区 19F
基线请求失败时,确实不能急着下结论。
上海市松江区 20F
统一返回不存在有助于减少对象枚举。
山东省烟台市 21F
审计日志能不能看懂,也是证据链的一环。
甘肃省 22F
风险判断还是要区分已验证和推测影响。
重庆市 23F
测试数据清理完成后最好再核对一次。
重庆市 24F
无害标记适合用来验证写操作权限。
浙江省舟山市 25F
前端藏得再深,服务端也得重新验权限
上海市松江区 26F
响应字段白名单比直接返回对象稳妥。