AI 建站后别漏查 Supabase 行级权限
文章提醒开发者检查 Supabase 的行级安全配置,避免浏览器中的公开密钥配合宽松策略造成越权访问。给出基于用户身份的策略示例、跨账号验证方法,以及让 AI 编程工具生成迁移的提示词。
AI 深度解读
Supabase 应用能正常登录、查询和保存,不代表数据库权限已经配置正确。原文提醒的是一个容易被功能测试漏掉的问题:浏览器里的 anon key 可以被任何人拿到,数据访问边界需要靠 Row Level Security(RLS,行级安全)来约束。
作者给出的处理办法很直接:检查每张表是否启用 RLS,再把访问策略绑定到当前用户。这篇没有攻击实验或损失数据,但它提出的检查方法,适合用来复查 AI 工具生成的应用。
anon key 公开,为什么数据不能跟着公开
前端连接 Supabase 时,浏览器需要拿到项目 URL 和公开的 anon key。用户可以在开发者工具里找到它们,然后绕过页面,直接请求数据库接口。
因此,隐藏按钮、限制路由、在前端查询里加上用户 ID,都不能代替数据库授权。别人可以自行构造请求,不经过你的页面逻辑。
anon key 可以公开,用户能访问哪些行必须由数据库决定。
原文把关闭 RLS 的后果概括为任何人都能读、改、删所有行。这里需要补一个前提:具体能执行哪些操作,还取决于表是否通过 API 暴露,以及对应角色获得了哪些数据库权限。关闭 RLS 会失去逐行授权保护,但不能仅凭这一项就断言所有操作必然开放。
启用 RLS 后,还要检查策略允许了什么
作者建议先进入 Supabase → Table editor,确认每张表都显示 RLS enabled,再添加与登录用户关联的策略,例如:
using (auth.uid() = user_id)
这段条件用于把当前用户身份与行里的 user_id 比较,限制用户访问属于自己的记录。要关注等号右侧的 user_id:它必须确实表示该行的所属用户,不能机械地给所有表套用。
只打开开关还不够。如果策略写成 using (true),条件永远成立,就没有通过这个条件筛选用户。作者建议仅在数据确实需要公开读取时,对读取策略保留这种写法。
原文对不同操作的语法说得不够严谨:INSERT 应使用 WITH CHECK 检查新行,UPDATE 还需要考虑修改后的记录是否满足约束。USING 示例能说明授权思路,却不是读写操作的完整策略。
作者还给出一个简单验证办法:登录一个用户,尝试读取另一个用户的数据。它比单纯检查页面是否正常,更接近真正需要验证的权限边界。
让 AI 修改时,要把迁移和数据保留写清楚
原文建议把下面的请求交给 Lovable、Bolt、v0、Replit 或 Cursor,让工具补上策略:
Enable Row Level Security on every table in the public schema, and add policies so each user can only read and change their own rows (auth.uid() = user_id), with public read access only where data is meant to be public. Add it as a new migration, and don't remove any existing data.
要关注最后一句:通过新的 migration(数据库迁移)提交修改,并保留已有数据。这样权限变更有明确的文件记录,也更方便审查。
不过,提示词没有提供业务权限模型。哪些表公开、哪些记录属于个人、哪些数据允许团队共享,仍然需要开发者明确。AI 生成 SQL 后,不能只以应用还能运行作为验收标准。
我的判断
作者说对了一个关键问题:功能测试通过,无法证明用户之间的数据已经隔离。对于把 Supabase 直接接到浏览器的应用,这个检查很有针对性。
但「每个用户只能访问自己的行」只适合部分业务。团队项目、共享文档、组织后台,通常需要按成员关系或角色授权,单一的 auth.uid() = user_id 无法覆盖这些规则。如果走 Node 服务端统一访问数据库,也要检查服务端自己的鉴权逻辑,不能照搬浏览器直连场景的结论。
原文没有给出实测过程、漏洞命中率,也没有比较不同修复方式的效果。文末介绍了作者参与的 Launchpad by Axonscape,宣称能从公开 GitHub 仓库或 .zip 代码执行免费上线检查且不保留内容;这些属于产品自述,原文没有提供验证依据。
能拿来做什么
- 复查前端直接访问的 Supabase 表,在 Table editor 确认 RLS 状态,并逐项检查读取、插入、更新和删除策略。
- 准备两个测试账号,用账号 A 的身份直接请求账号 B 的记录,分别验证读取、修改和删除是否被拒绝。
- 让 Cursor 等工具修复权限时,先说明个人数据、公开数据和共享数据的边界,再要求生成新的 migration,并审查策略是否符合这些规则。