Vibe Coding 越权负向测试:用主体、租户与资源矩阵证明默认拒绝
一个 owner 成功读取自己的项目,不能证明其他人读不到。本文构造主体、动作、资源、租户与状态矩阵,并同时断言响应、数据、副作用、缓存和日志没有发生越权。
本文要点
- 主题聚焦:AI、Vibe Coding、TypeScript
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
AI 为 /projects/:id 补了一条“已登录用户可读取项目”的测试,happy path 通过,覆盖率也上升;但测试只使用一个用户、一个租户和一条数据,handler 即使完全漏掉 tenant_id 条件也会绿色。授权的核心证据不是允许路径成功,而是相似但不被允许的主体无法得到对象、改变状态或触发副作用。
Vibe Coding 鉴权与租户隔离说明可信上下文、复合外键、缓存和 worker 如何建立架构边界。本篇不重复设计这些控制,只回答如何用可重放反例证明它们确实位于每条路径。
从授权规则生成矩阵
把案例表示为 主体 x 动作 x 资源 x 租户 x 资源状态 x 会话状态。主体至少含未登录、非成员、同租户低权限、同租户允许角色、不同租户同角色、已撤权成员和服务身份;动作覆盖读、列出、搜索、创建、更新、删除、导出、下载、批量与后台执行。资源状态加入存在、不存在、软删除、转移中和权限刚变化。
每个单元格必须来自已批准授权规则,预期是 allow、deny 或待澄清。待澄清不能由 AI 猜成允许,也不能为了测试绿色写成当前实现。用Vibe Coding 测试矩阵控制组合规模:先覆盖每条规则的最小反例,再对高风险维度做成对组合,不追求无意义的笛卡尔积。
ID 应不可预测,但测试仍要假设攻击者能取得一个真实异租户 ID。分别请求同租户不存在、异租户存在和全局不存在对象,检查状态码、响应大小、错误结构与明显时序差异符合统一的防枚举策略。403 或 404 由产品策略决定,关键是各端点一致且响应不包含标题、owner、内部 ID 或调试信息。
Fixture 必须能制造真正的冲突
为 A、B 两个租户建立同名项目、相同角色名称和交叉相似数据,确保过滤遗漏会返回“看起来合理”的错误对象。用Vibe Coding 场景化 Seed 与 Fixture固定身份、时钟和资源别名,不共享一个万能管理员。每例独立声明 actor、active tenant 与目标 resource,测试代码禁止从目标资源反推 actor 的租户,否则会在准备阶段自动修正越权输入。
负向断言至少有四层:HTTP/函数结果被拒绝;目标及同租户数据未变化;队列、邮件、Webhook、计费等副作用未发生;缓存、日志、trace 与错误中没有目标租户内容。列表和搜索还要断言结果全集的每条 tenant_id 都匹配,而不只断言某个已知异租户对象“不在第一页”。批量 API 混入一个异租户 ID 时,原子拒绝或逐项结果必须符合明确合同。
缓存测试先由高权限主体填充,再由低权限或另一租户读取同一资源 ID,防止缓存命中绕过策略。异步测试在入队后撤权,按任务合同断言取消或重新授权;服务凭证不能让 worker 忽略最终用户范围。连接池连续复用同一连接服务 A、B 租户,验证事务局部上下文不残留;若启用 PostgreSQL RLS,以应用角色而非表 owner 运行遗漏租户条件的查询。
会话变化需要单独制造竞态:用户在标签页 A 选择租户 A,标签页 B 切到租户 B;旧页面随后提交写请求,服务端不能仅信客户端保存的 active tenant。角色降级、成员移除、会话刷新和短期授权过期后,旧 access token、预签名下载地址与 GraphQL/DataLoader 缓存各自按合同失效。若系统允许短暂传播延迟,测试应绑定明确上限和期间的保守行为,而不是用任意 sleep 把竞态藏掉。
管理和支持入口也进入同一矩阵。impersonation 必须有独立批准、目标租户提示、动作范围和到期时间;退出代办后旧上下文不能继续写。所谓“内部接口”仍以真实服务身份和资源范围测试,网络位置不能替代授权。
证明测试不是陪实现演戏
对授权控制做安全变异:移除 repository 的租户条件、把角色比较放宽、跳过缓存命中的复核、让撤权代际不更新。对应测试必须失败且指出哪个对象越界;然后恢复实现。这个步骤可按Vibe Coding 的 AI 生成测试审查检查循环预言和过度 mock,避免测试调用与生产相同的 canAccess() 来计算 expected。
再用AI 生成代码的属性测试生成多租户对象与操作序列,核心不变量是任何成功返回、变更和副作用都属于当前授权范围。缩减后的失败样本保存为固定回归案例。属性测试补充未知组合,不替代可读的授权矩阵。
CI 报告绑定代码版本、策略版本、fixture 代际和应用连接角色,分别统计允许、拒绝、待澄清与未执行案例。新增端点、动作、角色或缓存层时,矩阵缺失直接失败;不能用一个全局“需要登录”测试继承通过。
越权测试完成的标准不是收到了几个 403,而是对每条资源级规则都有相邻反例,拒绝后所有可观察面保持干净,并且故意破坏控制时测试会红。这样 AI 才是在扩展一份可验证的授权合同,而不是围绕当前实现补几条容易通过的请求。
Related