第 6 章 · 03 IDOR 与越权(对象级 / 功能级 / 批量赋值) 本节摘要:访问控制漏洞不靠花哨 payload,靠的是「我能不能做本不该做的事」。本节把三类最常见的越权合并讲解:IDOR/BOLA(对象级授权失败)——把别人的对象 ID 换进请求就拿到/改了别人的数据;BFLA(功能级授权失败)——低权限者调用了本该只有管理员能调的端点/动作;Mass Assignment(批量赋值)——服务器把客户端提交的字段直接绑进模型/DTO,没有字段级白名单,导致 / / 被悄悄改写。
本节摘要:访问控制漏洞不靠花哨 payload,靠的是「我能不能做本不该做的事」。本节把三类最常见的越权合并讲解:IDOR/BOLA(对象级授权失败)——把别人的对象 ID 换进请求就拿到/改了别人的数据;BFLA(功能级授权失败)——低权限者调用了本该只有管理员能调的端点/动作;Mass Assignment(批量赋值)——服务器把客户端提交的字段直接绑进模型/DTO,没有字段级白名单,导致
role/isAdmin/ownerId被悄悄改写。三者共享同一套方法论:构建「主体 × 对象 × 动作」或「主体 × 动作」矩阵,至少两个身份(owner 与非 owner、basic 与 admin),在每一条传输通道(REST/GraphQL/gRPC/WebSocket)、每一种编码(JSON/form/multipart)上交叉验证授权是否一致。授权必须在「执行动作的服务边界」上,把主体、动作、具体对象三者绑定到每一次请求——UI 门槛、网关、先前步骤都不能替代。
内容来源:原项目知识包
strix/skills/vulnerabilities/{idor,broken_function_level_authorization,mass_assignment}.md,汉化并套用体系化模板合并。
⚠️ 仅限授权测试:本节所有 payload 与技术仅用于你自己的应用或有书面授权的渗透测试。未经授权对他人系统使用这些技术是非法的。
阅读完本节,你应当能够:
role/isAdmin/ownerId/plan/限额/特性开关/计费)。三类越权的攻击面各有侧重,但都围绕「引用/动作/字段未被绑定到调用方」。
作用域:
引用位置:路径、查询参数、JSON body、form-data、header、cookie;JWT claim、GraphQL 参数、WebSocket 消息、gRPC 消息。
标识符形态:整数、UUID/ULID/CUID、Snowflake、slug;复合键(如 {orgId}:{userId});不透明 token、base64/hex 编码 blob。
关系引用:parentId、ownerId、accountId、tenantId、organization、teamId、projectId、subscriptionId。
展开/投影开关:fields、include、expand、projection、with、select、populate——常在 resolver/serializer 里绕过授权。
高价值对象:导出/备份/报表端点(CSV/PDF/ZIP)、消息/邮箱/通知/审计日志/动态流、计费(发票/支付方式/交易/信用)、医疗/教育/HR/PII/PHI/PCI、管理员/员工工具与模拟、对象存储 key(S3/GCS 签名 URL、分享链接)、后台任务(导入/导出 job ID、任务结果)、多租户资源。
核心:调用方能调用本无权调用的函数/动作(端点、mutation、管理员工具)。常见于「不同传输/网关/角色之间校验不一致」或「服务信任客户端提示」。
X-User-Id/X-Role。高价值动作:角色/权限变更、模拟/sudo、邀请/接受加入组织;审批/作废/退款/发信用、价格/套餐覆写;导出/报表生成、数据删除、账户停用/激活;特性开关切换、配额/授权调整、许可证/席位变更;2FA 重置、邮箱/手机号验证覆写。
核心:客户端提交的字段被直接绑进模型/DTO,没有字段级白名单。常见于现代 API 与 GraphQL,导致提权、所有权变更、非授权状态转换。
isAdmin/role/roles[]/permissions[]/status/plan/tier/premium/verified/emailVerified)、所有权/租户(userId/ownerId/accountId/organizationId/tenantId/workspaceId)、限额/配额(usageLimit/seatCount/maxProjects/creditBalance)、特性开关(features/flags/betaAccess/allowImpersonation)、计费(price/amount/currency/prorate/nextInvoice/trialEnd)。💡 核心心法:授权必须在执行动作的服务边界,把主体、动作、对象(以及字段)绑定到每一次请求。引用是否不透明(UUID)无关紧要——拿到就能测;UI 门槛、网关、先前步骤都不能替代服务自身的检查。
参数分析:
page[offset]、page[limit]、cursor、nextPageToken——常泄露或接受跨租户/跨状态。arjun -u <url>(GET)或 arjun -u <url> -m POST 找未文档化参数,常翻出 ?include_deleted=1、?as_user=…、?owner_id=… 这类扩大 IDOR 面的隐藏过滤条件。枚举技巧:
{"id":123} vs {"id":"123"}、数组 vs 标量、对象 vs 标量。id=1&id=2、JSON 重复键 {"id":1,"id":2}(解析器优先级)。userId vs userid vs USER_ID;resourceId/targetId/account 等别名。/files/user_123/../../user_456/report.csv。UUID/不透明 ID 来源:日志、导出、JS bundle、分析端点、邮件、公开动态;基于时间的 ID(UUIDv1、ULID)在窗口内可能可猜。
profile.role/profile[role]/settings[roles][];重复键与优先级 {"role":"user","role":"admin"};稀疏/patch 格式(JSON Patch/JSON Merge Patch,尝试加入禁止路径)。横向与纵向访问:用同一 token 在不同主体间交换对象 ID 探横向;用低权限 token 重复探纵向;针对部分更新(PATCH、JSON Patch/JSON Merge Patch)做静默的非授权修改。
批量与批处理:批量端点(批量更新/删除)常只校验第一个元素;在数组中段插入跨租户 ID;引用外部对象 ID 的 CSV/JSON 导入可绕过创建时检查。
二级 IDOR:用 list/search 端点、通知、邮件、webhook、客户端日志收集合法 ID;直接获取或修改这些对象;操纵分页/游标跳过过滤,拉取他人页面。
Job/Task 对象:从一个用户拿 job/task ID 去取另一个用户的结果(export/{jobId}/download、reports/{taskId});引用他人 task ID 取消/审批其任务。
文件/对象存储:直接对象路径或弱作用域签名 URL;尝试 key 前缀变更、content-disposition 技巧、跨租户复用陈旧签名;用其他租户的 share token 替换;尝试大小写/URL 编码变体。
GraphQL:
query IDOR { me { id } u1: user(id: "VXNlcjo0NTY=") { email billing { last4 } } u2: node(id: "VXNlcjo0NTc=") { ... on User { email } } }
滥用 batching/aliases 单请求取多个用户节点;Relay 全局 node 模式下解码 base64 ID 并交换原始 ID;经特权类型上的 fragment 过度取数据。
微服务与网关:token 混淆——为服务 A 签发的 token 因共享 JWT 验证但缺 audience/claim 校验,被服务 B 接受;头部信任——反向代理/API 网关注入/信任 X-User-Id、X-Organization-Id,尝试覆写或删除;上下文丢失——异步消费者(队列、worker)重处理请求时不再复查授权。
多租户:经 header、子域、路径参数探测租户作用域(X-Tenant-ID、org slug);混合「token 的 org」与「另一 org 的资源」;测跨租户报表/分析汇总与管理视图。
WebSocket:每订阅授权——channel/topic 名不可被猜中(user_{id}、org_{id});subscribe/publish 校验必须服务端执行,不能只在握手;订阅自己 channel 后尝试带目标用户 ID 发消息。
gRPC:直接的 protobuf 字段(owner_id、tenant_id)常绕过 HTTP 层中间件;用不同主体的 token 经 grpcurl 验证引用。
集成:引用外部对象的 webhook/回调(如 invoice_id)未校验所有权即处理;第三方导入器因缺租户绑定把数据同步进错误租户。
动词漂移与别名:替代方法(GET 执行状态变更;POST vs PUT vs PATCH 差异;X-HTTP-Method-Override/_method);执行同一动作但校验更弱的替代端点(legacy vs v2、移动 vs Web)。
边缘 vs 核心不匹配:边缘拦截但核心服务 RPC 直接接受;经暴露的 API 路由或 SSRF 调内部服务;网关注入的身份 header 覆盖 token claim——提供冲突 header 测优先级。
特性开关绕过:客户端检查的特性门槛,直接调后端端点;仅管理员 mutation 在 UI 隐藏但仍存活,经 GraphQL 或 gRPC 工具调用。
mutation Promote($id:ID!){ a: updateUser(id:$id, role: ADMIN){ id role } }
批处理 job 路径:创建导出/导入 job 允许,但 finalize/approve 缺授权——finalize 他人的 job;重放执行特权动作但不校验调用方的 webhook/后台任务端点。
content-type 路径:JSON vs form vs multipart 用不同中间件——用最宽松的解析器发动作。
GraphQL/gRPC/WebSocket:resolver 级每 mutation/字段校验,别假设顶层 auth 覆盖嵌套 mutation 或管理员字段;滥用 aliases/batching 夹带特权字段,持久化查询有时绕过 auth 转换;gRPC 经反射列出 service/method,调用被网关隐藏的管理员方法;WebSocket 握手级 auth,确保特权事件(如 admin:impersonate)逐消息授权。
提权:注册/资料更新时设置 role/isAdmin/permissions;切换暴露的 admin/staff 标志。
所有权接管:改 ownerId/accountId/tenantId 占有资源;跨用户/租户移动对象。
特性门槛绕过:经 flags/features 字段启用 premium/beta/特性开关;抬高 seatCount/限额/配额。
计费与权益:改 plan/price/prorate/trialEnd 或 creditBalance;绕过服务端重算。
嵌套与关系写入:可写的嵌套 serializer 或 ORM 关系,允许在调用方作用域之外创建/关联对象。
GraphQL 专项:input 类型缺字段级授权——在 mutation input 尝试禁止字段;配合 aliasing/batching 对比效果;mutation 后立即用 fragment 过度取变更字段。
ORM 框架边界:Rails(accepts_nested_attributes_for 的深嵌套、strong parameters 配置错误);Laravel($fillable/$guarded 误用,guarded=[] 全开);Django REST Framework(可写嵌套 serializer、read_only/extra_kwargs 缺口、partial update);Mongoose/Prisma(schema path 未过滤、select:false 不挡写入、upsert 默认值)。
解析器与校验器缺口:校验器在绑定后运行且不覆盖额外字段;未知字段在响应里被静默丢弃但底层已持久化;移动/Web/网关之间白名单不一致——替代编码绕过校验流水线。
三类越权共享一套绕过思路,核心是「不同传输/编码/网关之间授权不一致」。
解析器与传输:
application/json ↔ application/x-www-form-urlencoded ↔ multipart/form-data。X-HTTP-Method-Override、_method=PATCH;或在错误接受状态变更的端点上用 GET。参数污染:查询/body 里重复参数影响服务端优先级(id=123&id=456),两种顺序都试;混合大小写/别名参数名让网关与后端不一致(userId vs userid)。
缓存与网关:CDN/代理 key 混淆——响应 key 不含 Authorization 或租户 header,把缓存对象暴露给其他用户;操纵 Vary 与 Accept header;重定向链与 304/206 行为可跨租户泄露内容。
头部信任(BFLA/Mass Assignment):提供 X-User-Id/X-Role/X-Organization header;移除或与 token claim 冲突;观察哪个来源胜出。
路由影子(BFLA):跳过新中间件链的 legacy/替代路由(/admin/v1 vs /v2/admin)。
幂等与重试(BFLA):重试/重放 finalize/approve 端点——应用状态却不每次校验执行者。
竞态窗口(对象级):time-of-check vs time-of-use——在并行请求中,于校验与执行之间改变引用 ID(详见第 06 节竞争条件)。
盲通道(对象级):用差异响应(状态、大小、ETag、时延)探测存在性;owned vs foreign 对象的错误形态常不同;HEAD/OPTIONS、条件请求(If-None-Match/If-Modified-Since)可在不取完整内容的情况下确认存在。
Mass Assignment 专属:
text/plain,某些代码路径只校验一种。确认一个越权真实存在的稳妥步骤:
常见误报:
read_only 且跨编码一致强制;仅 UI 端变化、无持久化效果。⚠️ 越权是 OWASP API Top 1(BOLA)与 Web Top 的常客。即便「只读」的对象级泄露,在合规与隐私层面也是高危;能改密码/邮箱/角色的越权等同于账户/系统接管。
arjun 找隐藏参数;列表/导出端点作 ID 种子;类型/边界值/重复键/大小写别名/二级 IDOR;GraphQL aliases/batching、gRPC reflection。下一节,我们看 SSRF——当服务器替用户去取远程内容,这条「隧道」就直通内网与云控制面。