Skip to content

security(authz): 多组织下伪造 organization_id 的 insert 可越租户墙 — Layer 0 未门控 insert post-image #2937

Description

@os-zhuang

背景

在 B1(ADR-0095 D1,PR #2933)落地 Layer 0 期间核实写路径时发现一处既有(非 B1 引入)的跨租户写缺口。B1 的 Layer 0 门控读 + update/delete 的 pre-image,但不门控 insert 的 post-image,因此没有顺带堵上它。单独立项跟踪,避免夹带进行为保持的 B1。

缺口

多组织模式(tenancy.mode='multi' + @objectstack/organizations)下,一个普通用户可以 insert 一条伪造 organization_id(指向别的租户)的业务记录,使其落进受害租户的数据里。三个条件叠加:

  1. organization_id 是普通可写 lookup 字段Field.lookup('sys_organization', …)),用户 insert 时可直接提供,未被系统字段守卫剥离。
  2. 企业版 auto-stamp 只在缺省时填充、不覆盖cloud/packages/organizations/src/organizations-plugin.ts:147
    if (data.organization_id == null || data.organization_id === '') {
      data.organization_id = opCtx.context.tenantId;
    }
    用户提供了伪造值 → 非空 → auto-stamp 不覆盖。
  3. 写侧从无租户 post-image 检查computeWriteCheckFilterplugin-security/src/security-plugin.ts:1981)只纳入显式带非空 check 子句的策略;种子 tenant_isolation 只有 usingcheck,故从不参与写检查。

结果:伪造 organization_id 的 insert 既不被 auto-stamp 纠正,也不被任何租户写检查拒绝。Layer 0 的 AND 过滤器作用于读和 update/delete 的 pre-image 匹配,但 insert 无 pre-image,未被门控。

影响

  • 仅多组织(企业版)部署受影响;单组织/物理隔离(env-per-DB, ADR-0002)不受影响。
  • 攻击者可向别的租户注入行(数据污染 / 潜在的钓鱼或配额攻击);注入的行对攻击者自己反而不可见(Layer 0 读过滤会隐藏它)。
  • open-core dogfood 跑不到多组织路径(需企业版 @objectstack/organizations),故自动化回归门未覆盖——需在企业版环境验证。

建议修法(是 net-new delta,需独立评审)

两处配合,任一单独都不足:

  1. Layer 0 增加 insert post-image 租户检查:insert 时校验 organization_id == ctx.organization_id(platform-admin 姿态 + 对象 posture 允许时豁免,与 D1 读侧同规则)。落点 plugin-security/src/tenant-layer.ts + computeWriteCheckFilter 接线。
  2. 企业版 auto-stamp 改为权威覆盖(或在 stamp 后校验):organizations-plugin.ts Middleware A 对租户对象始终ctx.tenantId 覆盖 organization_id,而非仅填充缺省。需确认是否有合法「代客设置 org_id」的 insert 场景(import/admin 工具、跨组织迁移)——若有,这些走 system context 或专用端点,不受用户上下文的覆盖影响。

注意这会引入一处新的行为 delta(此前能成功的「带 org_id 的用户 insert」会被拒正或被覆盖),需像 ADR-0095 的四处 delta 一样进 role×object×insert 矩阵门并写 release-notes callout。

关联

发现于 tracking #2920 的 B 组内核链开发过程中。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions