关于 db-multitenancy
db-multitenancy 是用于审查多租户数据库结构的技能,关注租户隔离和面向租户的性能。它检查模式定义,也可通过只读 PostgreSQL 查询验证数据库状态。
主要功能
- 01评估租户模型是否匹配需求
- 02查找缺失或可为空的租户列
- 03检查行级隔离是否得到强制执行
- 04验证键和索引是否以租户列开头
- 05检测跨租户外键和唯一约束
适用场景
- 01部署前审查多租户模式
- 02发现租户隔离薄弱的表
- 03识别无法按租户裁剪的索引
- 04验证 PostgreSQL RLS 和索引状态
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ hainrixz/ claude-db --skill db-multitenancy安装后在 Claude Code 中输入/db-multitenancy确认已加载。
详细介绍
db-multitenancy (M9)
在多租户系统中,租户边界是架构中最重要的约束:单个查询如果忘记限定租户,就会将一个客户的数据泄露给另一个客户。这也是一个性能维度——tenant_id 必须排在键和索引的首位,确保每次访问都能按租户筛选。本模块同时涉及两个维度:设计层面的隔离(安全性)和性能层面的索引/分区局部性(扩展性)。如果系统是单租户,请完全跳过(not_applicable)。
检查内容
- 租户模型是否适用:共享表(
tenant_id列)、每租户一个 schema,还是每租户一个数据库——如果模型与隔离要求不匹配,则标记(例如,对于要求严格隔离/受监管的工作负载,使用没有 RLS 的共享行)。 - 缺少租户范围限定:租户所属的表缺少
tenant_id/org_id/account_id列,或者租户列可为空(允许出现未限定租户的行)。 - 隔离未强制执行:仅依赖应用代码限定租户范围,既没有 RLS 策略(Postgres),也没有约束——漏掉一个
WHERE tenant_id = ?就会泄露数据。与 M10 相关(依赖的租户表关闭 RLS 时 = sev5)。 - 索引/键以
tenant_id开头:索引和 PK/聚簇键没有以tenant_id开头,因此引擎无法筛选到单个租户——会跨租户执行全表扫描(性能),隔离也更弱。与 M11 的 ESR 排序和 M16 分区相关。 - 跨租户 FK/UNIQUE:定义唯一约束或 FK 时未包含租户列,可能导致跨租户冲突/联接。
维度与严重性
- 依赖的共享租户表关闭 RLS/没有强制隔离:严重性 5,
fail,维度both,置信度established(上限;该上限本身由 M10 的 RLS 规则负责断言/执行——需协调,避免重复计数)。 - 租户所属的表缺少
tenant_id:严重性 4,warn/fail,维度both。 - 索引/PK 未以
tenant_id开头:严重性 3,warn,维度performance。 - UNIQUE/FK 未包含租户列:严重性 3–4,
warn,维度both。
Tier-0 静态检查
通过 scripts/parse-schema.mjs 解析 DDL/快照:识别租户所属的表(启发式方法:被标记为多租户系统中的业务表),检查每个表是否有非空租户列,验证 PK/UNIQUE/索引是否以该列开头,并检测已声明的 RLS 策略。对程序源代码的解析结果仍为 directional,绝不会触发 sev-5 上限。
Tier-1 验证查询
检查 RLS 状态和以租户列开头的索引:
-- $DATABASE_URL, read-only
SELECT relname, relrowsecurity, relforcerowsecurity
FROM pg_class WHERE relkind = 'r' AND relnamespace = 'public'::regnamespace;
-- is tenant_id the leading index column?
SELECT i.indexrelid::regclass AS index, a.attname AS first_col
FROM pg_index i
JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = i.indkey[0]
WHERE i.indrelid = '<table>'::regclass;
没有实时数据库时,RLS/强制执行相关发现保持为 needs_api,绝不能默认为通过。
发现项
按 schema/finding.schema.json 输出。示例:
M9.documents.rls_off_shared_tenant— 依赖 RLS 实现隔离的共享租户表已关闭 RLS(严重性 5,fail,维度both,置信度established)。M9.invoices.no_tenant_id— 租户所属的表没有tenant_id列(严重性 4,warn,维度both)。M9.events.index_not_tenant_leading— 索引以created_at而非tenant_id开头;无法按租户筛选(严重性 3,warn,维度performance)。 每项发现都应满足:evidence.observed原样引用 DDL/索引/策略;verification.reproduce是上文可运行的查询(method: ddl_parse/schema_introspect/index_check);expected_impact标注影响等级和置信度。
诚实原则
- sev-5 隔离上限要求该表确实被用于租户隔离;没有实时/已声明的 RLS 证据时,结果应为
needs_api或directional,绝不能默默触发上限。 - 应用层的
WHERE tenant_id = ?是实际的缓解措施,但并非数据库保证——漏掉一个子句就会泄露数据。应明确指出这一点;不要将其视为等同于 RLS。 - 单租户系统标记为
not_applicable(严重性 0,不计入评分)——绝不要凭空要求多租户能力。
评论
支持 Markdown