API1 实战:绕过对象 ID 授权漏洞归纳总结
对象级授权失效(IDOR),说白了就是用户能通过改个 ID 就访问别人家的数据。这漏洞太常见了,我在多次红蓝对抗中都遇到过。今天咱们就彻底把它掰开揉碎了讲清楚。
1. 漏洞本质
IDOR 的核心是:API 只验证了用户是否认证,却没验证用户是否有权操作该资源。换句话说,服务器相信了客户端提交的对象 ID,却没检查这个 ID 是不是属于当前用户的。
典型场景:用户 A 登录后访问 /api/users/123/profile,如果把 123 改成 124 就能看到用户 B 的个人信息,这就出事了。
2. 常见攻击手法
我在实际渗透中,总结出下面几种最常见的改 ID 姿势:
- 顺序数字枚举:最简单,直接递增或递减 ID。曾见过某电商的订单 API,从 1000000 枚举到 1000100,就找到了几十个其他人的订单。
- UUID/GUID 泄露:虽然理论上随机,但若在某处泄露了合法用户的 UUID(比如邮件、日志、前端源码),照样可以替换。
- 参数位置替换:不在路径里,可能在 POST 体、Cookie 或 Header 里。比如
{"userId": "admin"}。 - 非直接对象引用:有些系统用
seq=1002代替user_id=1002,但本质一样。
| 攻击向量 | 示例请求 | 风险等级 |
|---|---|---|
| 路径参数 | GET /api/v1/users/123/orders → 改为 124 | 高 |
| 查询参数 | GET /api/order?orderId=5678 → 改为 5679 | 高 |
| 请求体 | {"user_id": "abc"} → 改为 "def" | 中 |
| 自定义Header | X-User-Id: 100 → 改为 101 | 低(少见) |
3. 实战测试步骤
好,我们直接开干。以下是我个人习惯的测试流程,各位可以照着来:
- 先正常登录,用一个账号(比如 userA)操作基本 CRUD,抓包记录所有涉及对象 ID 的请求。
- 选一个关键请求,比如查看个人资料
GET /api/profile/42。 - 改 ID,把 42 换成 43 或 1 或 999999,观察响应。如果返回了其他人的数据,漏洞确认。
- 换方向测试:尝试 POST/PUT/DELETE 方法,替换 ID 看能否修改或删除别人的资源。
- 批量测试:用 Burp Intruder 或自定义脚本,从 1 开始枚举到某个范围,看哪些 ID 能返回不同内容。
小技巧:有些系统会返回「404 Not Found」而不是「403 Forbidden」,这是为了隐藏资源是否存在。但如果你发现某 ID 返回了有效数据而另一个 ID 返回 404,那就能确认 IDOR 存在。
4. 代码示例:用 Python 批量验证 IDOR
这是我写的一个小脚本,用来检测简单的数字 IDOR:
import requests
BASE_URL = "https://target.com/api/v1/users"
HEADERS = {"Authorization": "Bearer eyJ..."} # 替换为有效
def check_idor(start, end):
for uid in range(start, end + 1):
url = f"{BASE_URL}/{uid}/profile"
resp = requests.get(url, headers=HEADERS)
if resp.status_code == 200:
# 检查是否返回了登录用户自己的数据(比对我自己的 user_id)
data = resp.json()
if data.get("id") != uid:
print(f"[!] IDOR found! User {uid} returned other user's data")
else:
print(f"[+] User {uid} is self-data")
elif resp.status_code == 403:
print(f"[*] User {uid} blocked (403)")
elif resp.status_code == 404:
print(f"[*] User {uid} not found (404)")
else:
print(f"[?] User {uid} status {resp.status_code}")
# 从 1 测试到 100
check_idor(1, 100)
嗯,这里要注意:不能只看状态码。有些 API 对所有不存在或越权的 ID 统一返回 200 和一个空对象,这时就得比对数据内容。
5. 漏洞原理流程图
下面这张 SVG 图,把 IDOR 的攻击流程和防御要点串起来了:
6. 深入理解:为什么 IDOR 这么容易被忽略?
你想想看,很多开发团队在写 CRUD 接口时,容易陷入「功能正确」的思维定势。只要用户登录了能获取数据,就以为安全了。我曾在一个项目里看到,后端代码直接从 URL 参数拿用户ID去查数据库,连 session 里的 ID 都没校验。这就是典型的「认证通过即授权通过」的误区。
曾经有一次:我在审计某金融类 API 时,发现转账接口只校验了「发起人 有效」,却没校验「发起人是不是这个账户的持有者」。结果用 A 就可以给任意账户转账。这就是 IDOR 的变种——参数里有 from_account 和 to_account,但 from_account 根本不匹配 所属用户。
7. 真实案例分析
某知名社交平台曾爆出 IDOR:用户通过修改 /api/v1/users/{user_id}/photos 中的 user_id,可以查看任意用户的私密相册。攻击者只需要写个脚本枚举数字 ID 即可。漏洞根源:服务端只做了 if authenticated_user: 的判断,没有检查 if authenticated_user.id == requested_user_id。
8. 防御方案对比
| 层次 | 具体做法 | 优点 | 缺点 |
|---|---|---|---|
| 代码层 | 在控制器/处理函数中显式比对当前用户与资源所有者 | 可控性强,可定制 | 容易遗漏某个接口 |
| 中间件层 | 编写全局授权中间件,自动解析资源ID并验证 | 集中管控,减少重复代码 | 需要约定资源ID参数名 |
| 策略层 | 使用间接引用(如GUID)+ 随机令牌 | 让枚举变得困难 | 不能完全替代授权检查 |
| 架构层 | 实施 OAuth2 细粒度 scope + 资源服务器验证 | 标准化,易扩展 | 实现成本高 |
9. 总结要点
- IDOR 是 OWASP API Top10 排名第一的漏洞,因为太常见。
- 测试时不要只看状态码,要对比响应体内容是否属于其他用户。
- 防御核心:每个资源请求都必须做归属权校验,不要依赖前端参数。
- 附加措施:使用非连续 ID、日志审计、频率限制。
我个人习惯:在写 API 代码时,先写一个 check_resource_ownership(user_id, resource_id) 函数,然后所有涉及资源访问的接口都调用它。一开始就埋好这个钩子,后期审计也省心。