API 越权里,出镜率最高的名字之一叫IDOR(Insecure Direct Object Reference,不安全的直接对象引用)。名字有点学院派,翻译成人话却很土:
你拿着自己的登录态,把请求里的“对象编号”改成别人的,服务器居然把别人的东西给你了。
订单号、用户 ID、文件 ID、商户 ID、工单号、uuid看起来很随机……只要服务端没有认真问“这个对象属于你吗”,改编号就可能得手。扫描器对这类洞经常哑巴——它不懂“10086 是你的、10087 是他的”。所以 IDOR 几乎成了人工测试与业务安全的自留地。
一、IDOR 到底在说哪一种越权
1. 经典定义
应用使用用户可控的标识(ID)直接访问对象,却缺少或失败于授权校验,导致访问了未授权对象。
“直接对象引用”可以是:
- 数字 ID:
/orders/1024 - UUID:
/files/550e8400-e29b-41d4-a716-446655440000 - 文件名:
/download?name=contract.pdf - 预签名 URL 中的路径参数(若权限模型错误)
- GraphQL 的
id参数
UUID降低枚举,不自动消灭 IDOR——你若仍能通过泄漏的 UUID 访问他人对象,那仍是授权失败,只是攻击面从“遍历”变成“泄露后利用”。
2. 水平 vs 垂直
| 类型 | 含义 | 例子 |
|---|---|---|
| 水平越权 | 同级用户互访对方资源 | A 看 B 的订单 |
| 垂直越权 | 低角色访问高角色能力/资源 | 普通用户调管理删除 |
| 未授权 | 没登录也能访问 | 游客看需登录数据 |
IDOR 最常指水平对象级越权;广义报告里也有人把“改 role 参数”写成 IDOR,更严谨应写垂直越权/失效访问控制。检测手法高度重叠,定性时尽量准确。
3. 和“失效的访问控制”的关系
OWASP A01 大类下,IDOR 是最具体、最好教的一种。修好 IDOR,不等于访问控制满分,但能消掉一大片真实事故。
二、为什么 API 是重灾区
1. 前后端分离
前端不展示的按钮,API 依然在。移动端、小程序、第三方还共用一套后端。攻击者直接打 API,不走你的页面流程。
2. 对象 ID 满天飞
REST 风格天然/resource/{id}。列表接口先返回一堆 ID,详情接口再按 ID 取——若详情只验登录,IDOR 就成了“列表当字典 + 详情当仓库”。
3. 微服务与网关
网关验了 JWT,下游服务以为“网关都验了就信任”,只按 ID 查库。身份传到了,授权没传到。
4. 批量与导出
/orders?ids=1,2,3、导出任务、消息推送预览——一次越权一片数据。
5. 非 JSON 的“旁路对象”
PDF、发票、附件下载、WebSocket 订阅频道名、对象存储 key——都是对象引用,都要鉴权。
三、实战检测总流程(建议背下来)
① 准备账号矩阵(A/B,必要时商户/管理员) ② 摸清对象与 ID 出现位置(列表、推送、导出) ③ 对每个“带 ID 的读/写/删”做换绑测试 ④ 扩大到批量、导出、下载、共享链接 ⑤ 记录证据、定级、清理、回归用例化核心动作就一个字:换——换 ID、换账号、换角色、换租户。
四、账号与环境准备
1. 至少两套同级账号
A、B 权限相同,数据隔离。分别制造自己的订单/文档/消息。
记下:A 的order_id、B 的order_id。
2. 再加角色更好
普通用户、商户、运营、管理员——垂直问题一起捞。
3. 租户/组织
SaaS 多租户测org_id、tenant_id是否可改。这是 IDOR 的亲戚,常叫跨租户越权。
4. 工具
代理(Burp 等)、两个浏览器配置、可选的自动化脚本(只在授权范围)。
别对生产狂暴遍历 ID,以免触发风控或造成数据压力。
五、发现“对象引用”的实战技巧
1. 从列表到详情
登录 A,打开列表,抓详情请求。所有像 ID 的参数列成表:路径参数、Query、Body、Header(少见但有)。
2. 从 JS 与 APP 里挖隐藏 API
前端路由、Webpack 里的/api/、旧版接口。隐藏的getUserById往往鉴权更嫩。
3. 从通知与邮件挖 ID
邮件链接、短信链接、推送 deep link 带长 ID——测这些 ID 是否可在未分享情形下被他人使用。
4. 上传与下载
上传返回fileId,下载GET /file/{fileId}。用 B 的 token 下 A 的 fileId。
5. GraphQL / RPC
query { order(id: ...) { ... } }
mutation 里的 id。GraphQL 一次查询多字段,越权危害更大,检测逻辑一样:换 id + 换身份。
六、检测动作详解
1. 水平读越权(最常见)
- A 登录,访问 A 对象,存响应指纹(姓名、金额、标题);
- 同一请求,把 ID 改成 B 的,仍用 A 的 Token;
- 若返回 B 的指纹 → 读 IDOR 成立。
注意:有的返回 200 但空数据/统一报错,要区分“无权限”和“对象不存在”。以敏感字段是否出现为准。
2. 水平写越权
改地址、改订单备注、取消订单、修改共享权限。
用 A 的身份提交 B 的 ID。写成功比读成功更严重。
3. 删除与状态变更
删除别人的评论、归档别人的项目。状态机类操作都要测。
4. 创建时指定他人 ID
创建订单时 body 带userId;创建子账号时指定orgId。属“引用绑定越权”。
5. 批量接口
ids[]=B的id;user_ids列表。部分成功也要报。
6. 函数级:导出与异步任务
创建导出任务时带筛选条件,任务结果是否可被另一用户的task_id下载。
异步世界的 IDOR:task_id、job_id、report_id。
7. 存储与 CDN
返回的 URL 是否永久有效、是否无额外鉴权、是否可猜。
预签名过期短、且绑定权限,才有意义。
8. 编码与间接引用
ID 经 Base64、加密——若可解密或可替换密文仍过,继续测。
“加密 ID”不是授权。
七、自动化检测:能做多少、不能指望多少
1. 半自动
插件协助替换 ID、对比响应相似度,能提速。仍需人定义“哪些是对象”。
2. 基于流量的差分
用 A、B 账号分别爬业务,自动尝试交叉 ID。对标准化 REST 有效,对复杂业务易误报漏报。
3. 为什么很难全自动
- 不知道“同级对象”集合;
- 不知道响应里哪段算敏感成功;
- 验证码、验证码、步骤码打断;
- 合法共享会导致误报(A 本就可看 B)。
结论:IDOR 检测以方法论固化的人工/半人工为主,自动化是帮手。
八、合成案例集(方便内训)
案例 1:医保/电商订单详情 IDOR,泄露地址电话。
案例 2:工单系统附件fileId可跨员工下载。
案例 3:多租户 SaaS 改tenant_id读他租户报表。
案例 4:GraphQLuser(id)无授权指令。
案例 5:导出任务 ID 可枚举下载。
案例 6:对象存储 key 来自接口,下载接口未二次鉴权。
案例 7:“分享链接”永久有效且无密码,等于公开 IDOR。
案例 8:修改收货地址接口可改他人address_id。
每个案例的检测动作都是:双账号 + 换引用 + 看是否成功。
九、定级建议
| 情况 | 级别参考 |
|---|---|
| 可读大量 PII / 证件 / 订单 | 高危~严重 |
| 可写/删除他人关键对象 | 严重 |
| 跨租户数据 | 严重 |
| 仅可读非敏感公开类对象 | 中低(视业务) |
| 需先获取机密 UUID 且难泄漏 | 仍要修,可注明利用条件 |
“ID 难猜”不能当作接受风险的唯一理由,尤其当 ID 会在列表、日志、推送、Referer 里泄漏时。
十、修复与架构级解法
1. 服务端强制鉴权(根治)
伪代码思维:
obj = load(id) if obj.owner != current_user and not has_perm(...): deny每个对象类型集中写策略,避免每个 Controller 手写漏掉。
2. 统一授权中间件 / 策略引擎
基于注解:@PreAuthorize、自定义checkOrderAccess(orderId)。
GraphQL 字段解析器同样要挂授权。
3. 避免可信客户端
不要信 body 里的userId当所有者;所有者取自会话。
4. 间接引用可作辅助
每用户映射表、短时 tokenized reference——减枚举。仍需鉴权。
5. 批量接口
逐个做授权过滤;无权的 ID 不返回、不计入。
6. 下载与文件
鉴权后流式输出;或短时预签名且绑定用户与对象。
7. 多租户
所有查询带tenant_id = current_tenant强制条件(行级安全),禁止客户端传租户覆盖。
8. 日志与监控
授权失败告警;异常批量 403/交叉访问模式。
9. 测试门禁
CI 中集成双账号 API 测试:A token + B id → expect 403。
这是防回归最有效的手段。
十一、检测工作检查单(可直接贴到项目)
准备
- A/B 账号与各自对象 ID
- 代理可改包
- 范围与生产安全阀
覆盖
- 详情读
- 更新/删除
- 批量
- 导出/异步任务
- 附件下载
- GraphQL/RPC
- 租户字段
- 分享链接
证据
- 请求/响应(脱敏)
- 两个账号对比
- 复现步骤
收尾
- 通知开发
- 补回归用例
- 复测关闭
十二、常见翻车与误报
误报:家庭共享、企业委托、医生看病人——本身有授权关系。要先懂业务。
漏报:只测了读,没测写;只测了 REST,没测下载;只测了数字 ID,没测任务 ID。
假修复:前端隐藏 ID;改成 UUID 但仍不鉴权;仅网关验登录。
假安全:“我们 ID 是加密的”。
十三、收尾
IDOR 的检测没有神秘公式,只有重复一千次仍然有效的动作:
用自己的身份,访问别人的对象引用。
API 让这个动作更简单,也让疏漏更致命。防住它也不靠改成 UUID 的心理安慰,而靠每一条对象访问链路上的强制授权,以及双账号回归测试这道门禁。
今晚若只做一件事:选一个最核心的详情 API,准备两个账号各造一条数据,把 A 的 token 配上 B 的 id 打一次。
200 且是 B 的数据——恭喜你挖到了真洞;403——恭喜你们对象鉴权可能真的在线。无论哪种,你都已经在做“API 越权实战检测”,而不是在背 IDOR 的英文全称。
附:快速对照
| 问题 | 检测动作 | 修复 |
|---|---|---|
| 水平读 | A token + B id | 对象级鉴权 |
| 水平写 | A token 改 B 对象 | 同上 + 审计 |
| 批量 | 混入他人 id | 逐条过滤 |
| 下载 | 换 fileId | 鉴权后输出 |
| 租户 | 改 tenant_id | 强制租户条件 |