第三章 API1:失效的对象级授权 — 原理与测试
对象级授权漏洞,说白了就是攻击者能通过修改请求中的某个 ID,访问到他不该看到的资源。比如你改一下 URL 里的 user_id=1001 为 user_id=1002,结果别人的订单、病历、银行流水全出来了。这就是对象级授权失效。
在 OWASP API Security Top 10 里,这个漏洞排第一位,你想想看为什么?因为它在真实世界里太常见了。我个人做过上百场 API 渗透测试,几乎每次都能挖到几个这种问题。很多团队把精力都花在 SQL 注入、XSS 上,反而把最基础的授权检查给漏了。
3.1 漏洞的本质
对象级授权指的是:API 在处理某个具体资源(比如一个文件、一条订单、一个用户信息)之前,必须先验证当前用户对这个资源有没有操作权限。说白了就是问一句:“你有资格动这个 ID 的东西吗?”
但很多开发者只做了功能级授权(比如判断是不是“管理员”角色),没有检查特定对象是否属于这个用户。举个例子:
- 用户 A 只能访问自己的订单,但 API 只检查了 “用户已登录”,没检查 “订单 owner == 当前用户”
- 攻击者遍历订单 ID 1 到 10000,就能把全站订单信息拖走
关键区分
对象级授权 ≠ 功能级授权。功能级授权管的是“能不能调用这个接口”,对象级授权管的是“能不能访问这个接口里的某个具体 ID 资源”。两个都要做,缺一不可。
3.2 常见攻击场景
我在项目中遇到过无数次类似的案例,给你列几个典型场景:
| 场景 | 攻击方式 | 影响 |
|---|---|---|
| 水平越权(横向) | 修改其他用户的 ID | 查看/修改他人数据 |
| 垂直越权(纵向) | 低权限用户访问高权限接口 | 执行管理员操作 |
| IDOR(不安全的直接对象引用) | 直接枚举数字 ID | 批量数据泄露 |
| 批量赋值 | 在请求体中添加不属于自己的对象 ID | 篡改归属关系 |
这里面最恶心的其实是水平越权,因为你很难用传统 WAF 检测。攻击者发的请求完全合法,只是把参数里那个 ID 换了一下。
3.3 测试方法论
我个人习惯做对象级授权测试时,按下面几步走,效率很高:
- 信息收集 — 记录所有带有 ID 参数的端点,比如
/api/users/{id}、/api/orders/{orderId}、/api/files?fileId=xxx。 - 创建两个测试账号 — 账号 A 和账号 B,分别获取它们的合法资源 ID。
- 替换测试 — 用账号 A 的 去访问账号 B 的资源
/api/orders/456,看能不能成功。 - 遍历测试 — 尝试把 ID 从1开始递增,或者用 UUID 格式的也试随机值,看是否有未授权响应。
- 方法测试 — 同样的 ID 换不同的 HTTP 方法(GET、PUT、DELETE、PATCH),有时候 GET 访问不了,但 DELETE 却可以。
这里有个小技巧:很多 API 在 JSON 请求体或者嵌套结构里也藏着 ID,比如 {"order": {"id": 123, "items": [...]}}。别忘了测试这些隐蔽的参数。
避坑指南
我曾经遇到过一个案例,API 对 GET /api/records/123 做了授权检查,但是 POST /api/records/batch 传一个 ID 列表时没做逐项检查。结果攻击者通过批量接口一次性拖走了 5000 条记录。记住:批处理接口的授权检查不能只在入口做,要对每个对象做。
3.4 实战测试示例
假设目标是一个电商 API,我们先注册两个用户 user_a 和 user_b,拿到各自的 。
# 使用 user_a 获取自己的订单
GET /api/orders/1001
Authorization: Bearer _of_user_a
# 正常返回:HTTP 200,订单数据
# 现在换成 user_b 的 ,但访问 order 1001
GET /api/orders/1001
Authorization: Bearer _of_user_b
# 如果返回 HTTP 200 + 订单数据 => 存在对象级授权漏洞!
如果改成 PUT /api/orders/1001 并且 body 里修改了收货地址,结果也能成功更新 user_a 的订单,那就是更严重的问题——不仅越权读取,还能越权写入。
我一般还会测试 URL 参数和 body 同时传 ID 的场景,比如 POST /api/cart/addItem?cartId=200 同时 body 里有 {"productId": 999},看后端到底用的哪个 ID 做授权判断。
测试提示
如果 ID 是 UUID 格式,别放弃测试。很多系统生成的 UUID 是按时间戳+MAC地址算出来的,在一定程度上仍有可预测性。而且即使不可预测,只要后端没做授权检查,攻击者通过别的渠道拿到一个 ID(比如从公开的评论里看到订单号)就能直接访问。
3.5 知识体系框架
下面这张图总结了本章的核心逻辑:从对象级授权失效的根源,到攻击路径,再到测试步骤和防护要点。
3.6 如何防范
说实话,修复这个漏洞本身并不复杂,难的是在代码的每个角落都覆盖到。说说我的建议:
- 统一授权中间件 — 写一个类似
checkObjectAccess(user, resourceType, resourceId)的函数,所有处理具体资源的接口都调用同一个逻辑。 - 永远不要信任客户端传的 ID — 资源 ID 应该从 session 或 中提取用户身份,再通过数据库关联查询来获取合法 ID,而不是直接依赖客户端提交的参数。
- 批量操作逐条校验 — 如果 API 接收一个 ID 列表,循环里每条都要调用授权检查,不能只在进入循环前检查一次“当前用户是否有批量操作权限”。
- UUID 不是银弹 — 就算用了不可预测的 UUID,如果授权检查缺失,攻击者只要从某个泄露渠道拿到一个 ID 就能越权。授权逻辑不依赖 ID 的可猜测性。
快速检查清单
写完一个带 ID 参数的新接口,问自己三个问题:
- 这个 ID 对应的资源,当前用户有权访问吗?
- 如果换成别的 HTTP 方法(比如 DELETE),检查还在吗?
- 如果 ID 出现在请求体里,检查还在吗?
3.7 本章小结
对象级授权失效排名 API 漏洞之首,一点不冤。我做了这么多年渗透,几乎每个被测系统都能找到至少一个这种问题。你只要在写接口时多花几秒钟问一句“这个资源的 owner 是谁”,就能防住大部分攻击。
嗯,测试的时候记得把 IDOR 和水平越权捆绑着测,特别是那些嵌套 JSON 里的 ID、批量接口里的 ID 列表,往往是最容易遗漏的角落。我曾经就因为漏测了批量接口,后来客户上线第一天被人拖了库。从那以后我再也不敢跳过批量接口的授权测试了。