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。如果它出现在客户端代码中,你写的所有策略都形同虚设——任何持有它的人都对你的数据库拥有完全的读写权限。
如何修复
在 Supabase SQL 编辑器中运行以下语句,列出对外暴露的 public schema 中仍关闭 RLS 的所有表。控制台中的 Security Advisor 会以 "RLS disabled in public" 报告同样的问题。
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;为每张表开启 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 上设置各自的策略。
完全按攻击者的方式查询你的 API:只用公开 key,不带任何用户会话。如果返回了本不该公开的行,说明你的策略过于宽松。
curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-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 则是严重泄露。