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"中
自定义HeaderX-User-Id: 100 → 改为 101低(少见)

3. 实战测试步骤

好,我们直接开干。以下是我个人习惯的测试流程,各位可以照着来:

  1. 先正常登录,用一个账号(比如 userA)操作基本 CRUD,抓包记录所有涉及对象 ID 的请求。
  2. 选一个关键请求,比如查看个人资料 GET /api/profile/42。
  3. 改 ID,把 42 换成 43 或 1 或 999999,观察响应。如果返回了其他人的数据,漏洞确认。
  4. 换方向测试:尝试 POST/PUT/DELETE 方法,替换 ID 看能否修改或删除别人的资源。
  5. 批量测试:用 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 的攻击流程和防御要点串起来了:

API1 — 绕过对象ID授权漏洞(IDOR)攻击与防御流程 攻击者:篡改对象ID 步骤1:拦截合法用户请求(如 GET /api/profile/42) 步骤2:将ID改为其他值(如 43、1、admin) 步骤3:重放请求,观察响应差异 结果:若返回他人数据 → 确认IDOR漏洞 防御:对象级授权检查 措施1:从 /Session 提取当前用户ID 措施2:将请求中的ID与当前用户ID比对 措施3:若不匹配 → 返回403或404 额外防御:使用间接引用 (如GUID代替自增ID) 关键点 ✅ 必须始终验证用户是否对请求的资源拥有操作权限 ✅ 不要相信客户端提交的任何标识符(ID)

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) 函数,然后所有涉及资源访问的接口都调用它。一开始就埋好这个钩子,后期审计也省心。