授权 Web API 渗透测试教程:从攻击面梳理到越权验证与整改复测

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
279
文章
0
粉丝
渗透测试1 50字数 4067阅读13分33秒阅读模式
AI智能摘要
AI 生成的文章内容摘要

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

授权 Web API 安全评估流程示意图

一、先确认授权边界和停止条件

测试开始前,应将授权内容整理成可执行的测试规则,而不是只保留一封笼统的“允许测试”邮件。至少确认以下事项:

  • 目标范围:域名、IP、API 网关、环境名称、版本或租户范围。
  • 测试时间窗:开始和结束时间,是否避开批处理、发布、结算等高风险时段。
  • 允许的方法:是否允许身份与权限测试、错误配置检查、限速观察,以及是否禁止上传、删除、支付和管理操作。
  • 测试账号:普通用户、受限用户、管理员或租户管理员等角色,账号应为专门创建的测试账号。
  • 数据限制:只能访问授权测试数据;发现真实个人信息、令牌、密钥或生产数据时应立即停止扩大验证。
  • 影响阈值:请求速率、并发数、单次返回量、失败次数和资源消耗上限。
  • 停止条件:出现服务异常、数据写入、跨租户数据、敏感凭据、告警升级或无法判断影响时,暂停请求并联系授权方。
  • 清理方式:测试产生的数据、会话、临时对象和配置变更由谁清理,如何确认清理完成。

OWASP Web Security Testing Guide 将 API 侦察视为独立测试环节,强调先了解目标和 API 结构,再开展后续验证。可以将 OWASP WSTG 的 API 侦察章节 作为方法参考,但具体测试仍应服从项目授权范围。

二、建立资产和接口清单

API 测试的第一个实际产物应是接口清单,而不是扫描器报告。清单用于回答“测试了什么”和“还有什么没有测试”。

可以从以下资料来源建立清单:

  1. OpenAPI、Swagger、GraphQL Schema 或内部接口文档;
  2. 前端 JavaScript、移动端测试包和业务流程中的请求记录;
  3. API 网关路由、反向代理配置和服务注册信息;
  4. 测试环境访问日志、调用方清单和版本发布记录;
  5. 授权范围内的被动流量观察结果。

建议为每个接口记录:

字段示例
接口标识GET /api/v1/orders/{order_id}
环境staging-api.example.test
认证方式Bearer 测试令牌
预期角色普通用户
对象范围当前用户所属租户
操作类型读取
数据敏感度订单基础信息
是否产生副作用
测试状态未开始、通过、异常、需复核
证据位置请求编号、响应摘要、日志编号

清单中的目标应使用占位域名,例如 https://api.example.test,不要在报告或示例中泄露真实生产域名、内部主机名和令牌。对于文档中存在但实际不可用的接口,应记录“未验证”及原因,不能直接标记为安全。

三、先写出身份与权限假设

越权验证的核心不是随意替换参数,而是先定义服务端应该执行的授权规则。没有预期规则,就无法判断响应差异究竟是漏洞、业务设计还是测试误判。

1. 对象级授权假设

对象级授权关注“当前身份能否访问这个具体对象”。例如:

  • 用户甲只能读取自己创建的订单;
  • 租户甲的成员不能读取租户乙的项目;
  • 普通用户只能修改自己名下的地址;
  • 归档对象只能由具备特定职责的角色查看。

测试前准备两个同权限、不同对象范围的账号,例如 user_auser_b,并在测试环境中准备各自独立的对象:

  • order_a 属于 user_a
  • order_b 属于 user_b
  • 两个对象状态和字段结构尽量相似。

验证目标不是“把 ID 改大或改小”,而是确认服务端是否根据当前会话重新判断对象归属。对象标识可以是数字、UUID、路径片段、查询参数或请求体字段,均不应被客户端输入自动视为授权依据。

2. 功能级授权假设

功能级授权关注“当前角色能否执行这个动作”。例如:

  • 普通用户可以查看自己的订单,但不能导出全租户订单;
  • 项目成员可以读取项目,但不能修改成员权限;
  • 运维角色可以查看运行状态,但不能变更业务数据;
  • 管理功能不能仅凭隐藏按钮或前端路由限制。

功能级测试应覆盖同一接口在不同角色下的行为,并区分:

  • 未登录;
  • 已登录的普通用户;
  • 已登录的受限角色;
  • 具备管理权限的测试账号。

不要仅以 HTTP 状态码判断结果。200201204403404401 都需要结合响应体、业务状态、审计日志和服务端实际变化解释。

四、采用最小化请求进行验证

低影响验证的原则是:一次只验证一个假设,优先使用只读接口,控制请求频率,不主动扩大数据量。

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。这两种设计都可能合理,关键是未授权请求不能泄露对象内容,也不能产生未授权的状态变化。

五、重点检查认证状态和敏感数据暴露

认证状态

至少验证以下状态:

  1. 完全不带认证头;
  2. 使用格式错误或过期的测试令牌;
  3. 使用账号 A 的令牌访问账号 B 的对象;
  4. 令牌被撤销或退出登录后再次访问;
  5. 不同 API 版本或网关路径是否使用一致的认证策略。

应检查认证是否仅由前端控制、是否存在某个版本路径遗漏认证、是否将用户标识从请求参数直接作为可信身份。测试中不要尝试猜测真实令牌,也不要收集非测试用户的会话信息。

敏感数据暴露

即使对象级授权正确,也可能存在对象属性级问题。检查响应是否返回业务不需要的字段,例如:

  • 密码哈希、访问令牌、刷新令牌;
  • 内部服务地址、调试堆栈和数据库标识;
  • 身份证件、支付信息或完整联系方式;
  • 未脱敏的日志、密钥或第三方凭据;
  • 不同角色不应看到的管理字段。

验证时只需确认字段是否出现、是否可由低权限账号获取,不应下载大量记录或进一步使用暴露的凭据。发现真实敏感数据后,应停止扩大读取范围,保留最小必要证据,并按约定通知授权方。

不同权限账号进行 API 对象级授权对照验证

六、控制请求影响和测试节奏

API 测试不应把并发压测、批量枚举和破坏性操作混入普通权限验证。建议采取以下控制:

  • 优先使用 GETHEAD 或明确无副作用的接口;
  • 每次只发送一个或少量对照请求,并设置合理间隔;
  • 不使用大范围 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、个人信息、支付数据或可直接使用的密钥。截图也应进行遮罩,并说明截图时间、账号角色和环境。

八、风险判断不能只看状态码

越权问题的严重程度取决于对象和动作,而不是单一的 200403。可以从以下维度判断:

  • 是否跨用户、跨团队或跨租户;
  • 读取的是普通业务信息还是身份、财务、凭据等敏感数据;
  • 仅能读取,还是能够新增、修改、删除或授权;
  • 是否需要已认证账号,还是完全未认证即可访问;
  • 是否存在稳定、可重复的权限绕过;
  • 是否可能影响大量对象,但不应通过批量请求进行实际验证;
  • 是否已有审计日志和告警,能否追溯异常访问。

风险描述应区分“已证明的影响”和“合理推断的影响”。例如,单个测试对象被读取可以证明对象级授权失效;但不能据此断言整个数据库已被读取,除非有授权范围内的独立证据。

九、修复建议应落到服务端控制点

常见修复方向包括:

  1. 在服务端根据认证主体重新加载对象,并执行租户、用户和角色授权检查;
  2. 将授权逻辑放在业务服务或统一策略层,而不是只依赖前端隐藏按钮;
  3. 对读取、创建、更新、删除和导出分别定义权限;
  4. 使用字段白名单和角色化响应模型,避免直接序列化内部对象;
  5. 对管理接口、调试接口和旧版本 API 采用一致的认证与授权策略;
  6. 对失败访问记录主体、对象、动作、来源和结果,同时避免日志泄露令牌;
  7. 对高敏感操作增加重新认证、审批或二次确认;
  8. 根据业务需要设置分页上限、速率限制和资源消耗控制。

随机化对象标识只能降低枚举便利性,不能替代服务端授权。隐藏路径、修改前端路由或依赖网关规则,也不能替代每个业务接口自身的权限判断。

十、整改复测要证明“问题已关闭”

复测应复用原始测试条件,并保留修复前后的对照:

  1. 使用原账号 A 访问原对象 order_b
  2. 确认服务端拒绝访问或返回统一的无对象结果;
  3. 使用合法账号访问自身对象,确认正常业务没有被误伤;
  4. 检查读取、更新、删除、导出等相关动作是否同时修复;
  5. 检查不同 API 版本、网关路径和缓存层是否仍有差异;
  6. 检查响应中是否仍暴露不必要字段;
  7. 通过审计日志确认拒绝决策和主体信息正确;
  8. 清除复测产生的测试对象、令牌和临时配置。

复测结论可分为:

  • 已修复:越权请求被拒绝,合法请求正常,日志和字段控制符合预期;
  • 部分修复:某个方法或版本已修复,但仍有相关入口可访问;
  • 未修复:原始条件下仍可重复;
  • 无法验证:环境、账号、数据或日志条件不足,应明确列出缺失项。

最终,Web API 渗透测试应形成一条完整证据链:授权范围定义了可以测试什么,接口清单说明测试覆盖了什么,权限假设说明为什么该访问应被允许或拒绝,对照请求证明实际差异,日志和响应确认影响,整改复测则证明修复没有只停留在前端或单一路径。以这条证据链为主线,越权验证才会真实、可复核,也更值得开发和运维团队投入修复资源。

 
枫少@KillBoy
    • 回家的路灯
      回家的路灯 1

      先把授权边界写清楚,确实省很多麻烦

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证