● CWE-862 · OWASP A01:2021

Supabase 与 Firebase 访问规则(CWE-862):缺少行级安全如何暴露数据以及如何修复

Supabase 和 Firebase 应用允许浏览器用公开密钥直接查询数据库,所以挡在访客和每一行数据之间的只有行级安全(RLS)或安全规则。用 SQL 创建却没有 enable row level security 的表,或者停留在 allow read, write: if true 的 Firestore 规则,任何人都能读写。本文用 PostgreSQL 迁移示例说明 USING 和 WITH CHECK 策略如何把数据行限定在已登录用户范围内。

通俗易懂的原理解析 (ELI5)

一栋写字楼给每位访客发同一张大堂门卡,就印在宣传册的背面。只要每层楼的门在打开前都核对你的身份,这就没问题。有一层装修得很匆忙,门上根本没有读卡器,于是大堂门卡就能打开它,谁都可以进去翻看文件。Supabase 的 anon 密钥就是这张大堂门卡,它本来就是公开的。行级安全就是每层楼门上的读卡器。修复方法是给每扇门都装上读卡器,并明确告诉它每个人可以看谁的文件。

核心概念与专有名词

公开的 anon 密钥
Supabase 的 anon 密钥和 Firebase 的 Web 配置都打包在应用里,任何人都能复制。它们标识的是项目而不是用户,所以不是秘密,单靠它们无法保护数据。
行级安全(RLS)
PostgreSQL 的一项功能,会让每个查询都经过策略过滤。Supabase 的 Table Editor 会为新表启用它,但用 SQL 或迁移创建的表默认关闭 RLS,而默认授权又允许 anon 和 authenticated 读取和修改每一行。
USING 与 WITH CHECK
USING 决定用户可以查看、更新或删除哪些已有行;WITH CHECK 决定用户可以写入哪些新行或修改后的行。插入或更新策略如果只有 USING,用户就能为别的组织写入数据。
service_role 密钥
service_role 密钥会完全绕过 RLS。它只能放在服务器和边缘函数中,绝不能出现在浏览器或移动应用里。
Firestore 测试模式规则
以测试模式创建的项目会得到在某个日期前允许所有读写的规则,很多项目之后又改成了 allow read, write: if true。规则必须把 request.auth.uid 与文档的所有者字段进行比对。

攻击执行流程分解

步骤 1

攻击者打开应用

Supabase 的 URL 和 anon 密钥就在 JavaScript 包或移动应用安装包里。

步骤 2

他直接查询数据表

他只带着 anon 密钥调用 invoices 的 REST 接口,完全绕过应用界面。一个用 anon 密钥、不带用户会话访问测试项目的请求演示了这种情况。

步骤 3

数据库返回了所有行

RLS 处于关闭状态,而 anon 拥有 select 权限,所以 PostgreSQL 不做任何过滤,返回了所有组织的发票。

步骤 4

数据泄露或被篡改

如果还授予了 update 和 delete,攻击者还能修改或删除记录。2025 年,研究人员发现某个 AI 应用生成工具生成的 170 多个应用因缺少 RLS 而暴露数据,随后该问题被分配了 CVE-2025-48757。

源代码对比:漏洞与安全实现

存在漏洞的实现
-- migration.sql:这张表从未启用 RLS
create table public.invoices (
  id bigint generated always as identity primary key,
  org_id uuid not null references public.orgs (id),
  customer text not null,
  amount_cents bigint not null
);

-- 公开的 anon 密钥可以读取、修改和删除每一行
grant select, insert, update, delete on public.invoices to anon, authenticated;
加固后的安全修复
-- migration.sql:启用 RLS,收回 anon 权限,每种命令一条策略
create table public.invoices (
  id bigint generated always as identity primary key,
  org_id uuid not null references public.orgs (id),
  customer text not null,
  amount_cents bigint not null
);

-- 启用 RLS 后,没有策略就没有访问权限
alter table public.invoices enable row level security;
revoke all on public.invoices from anon;

-- 用户只能看到自己所属组织的发票
create policy "members read their org's invoices"
  on public.invoices for select to authenticated
  using (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));

-- 新行必须属于用户所在的某个组织;不提供 update 和 delete 策略
create policy "members add invoices to their org"
  on public.invoices for insert to authenticated
  with check (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));

工程与系统安全加固清单

参考来源

← 浏览完整的安全目录 全部漏洞指南 →