全部指南
严重程度:严重数据库安全

Supabase Row Level Security(RLS):锁住你的数据表

Supabase 通过其 Data API 把你的 Postgres 表直接暴露给浏览器,使用公开的 anon(publishable)key 进行认证。Row Level Security(RLS,行级安全)策略决定每个用户可以读取或修改哪些行。public schema 中没有启用 RLS 的表,任何打开你网站的人都能读取——而且往往还能写入。

这是什么

每个 Supabase 项目都带有一个 anon(或 sb_publishable_)key,它本就是放在前端使用的。它标识的是你的项目,而不是你的用户——任何人都能从你的 JavaScript 打包文件中复制它,自己向 https://<project>.supabase.co/rest/v1/ 发送请求,完全绕过你的 UI。

Row Level Security 是 Postgres 的一项功能,它为表附加策略:在每一次 SELECT、INSERT、UPDATE 和 DELETE 时对每一行检查的 SQL 条件。启用 RLS 但没有任何策略时,通过 API 什么都访问不到。你添加的每条策略只打开一扇门——例如“已登录用户可以读取 user_id 与自己 id 相符的行”。

为什么重要

缺失或过于宽泛的策略不是小 bug,而是一次完整的数据泄露:用户邮箱、个人资料、订单、私信——表里有什么,一条 curl 命令就能全部导出。AI 编程工具经常通过 SQL 迁移创建表,而这种方式默认不开启 RLS,它们还会生成像 using (true) 这样宽松的占位策略,只为了让应用能跑起来。

service_role(sb_secret_)key 会完全绕过 RLS。如果它出现在客户端代码中,你写的所有策略都形同虚设——任何持有它的人都对你的数据库拥有完全的读写权限。

如何修复

找出没有启用 RLS 的表

在 Supabase SQL 编辑器中运行以下语句,列出对外暴露的 public schema 中仍关闭 RLS 的所有表。控制台中的 Security Advisor 会以 "RLS disabled in public" 报告同样的问题。

select tablename
from pg_tables
where schemaname = 'public'
  and rowsecurity = false;
启用 RLS 并编写仅限所有者的策略

为每张表开启 RLS,然后为每种操作各添加一条策略。把策略限定在 authenticated 角色上,并将行的所有者列与 auth.uid() 比较。插入和更新需要 with check,防止用户写入属于他人的行。

alter table public.profiles enable row level security;

create policy "Users read own profile"
  on public.profiles for select
  to authenticated
  using ((select auth.uid()) = user_id);

create policy "Users update own profile"
  on public.profiles for update
  to authenticated
  using ((select auth.uid()) = user_id)
  with check ((select auth.uid()) = user_id);
避开常见的策略错误

用真正的条件替换 using (true) 这类占位策略。不要基于 user_metadata 做授权——用户可以自行修改它;请改用 app_metadata 或角色表。视图会绕过 RLS,除非创建时设置了 security_invoker = true;存储桶也需要在 storage.objects 上设置各自的策略。

用 anon key 从外部测试

完全按攻击者的方式查询你的 API:只用公开 key,不带任何用户会话。如果返回了本不该公开的行,说明你的策略过于宽松。

curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
  -H "apikey: <anon-key>" \
  -H "Authorization: Bearer <anon-key>"
把 service_role key 留在服务端

只在服务端代码(例如 API 路由或 Edge Functions)中使用 service_role(sb_secret_)key,绝不要放在 NEXT_PUBLIC_ 或 VITE_ 前缀后面。如果它曾被发送到浏览器,请在控制台中轮换它。

常见问题

我的 Supabase anon key 在浏览器中可见,这安全吗?

安全——anon(publishable)key 本就是设计为公开的,但前提是每张对外暴露的表都启用了 RLS 并配置了正确的策略。key 本身不会授予任何策略之外的权限。

我启用了 RLS,现在应用返回空结果。是坏了吗?

这正是 RLS 在按预期工作:没有策略时,每一行都会被拒绝。请为应用需要的操作添加策略,并限定到正确的用户,而不是再次关闭 RLS。

AppSafe 会测试我的 RLS 策略吗?

AppSafe 不会登录你的数据库,因此无法评估你的策略——请使用上面的 SQL 和 curl 检查。但它会扫描你的 JavaScript 中的 Supabase key。如果报告了某个 key,请解码 JWT 的中间部分并查看 role 声明:anon 出现在浏览器中是正常的,service_role 则是严重泄露。

你的应用受影响了吗?

AppSafe 一次免费扫描,就能检查这个问题以及数十项其他问题。

免费扫描我的应用