supabase 是做什么的?
核心原则
1. Supabase 频繁更新 —— 在实施前务必对照变更日志和当前文档进行验证。 不要依赖训练数据中的 Supabase 功能特性。函数签名、config.toml 设置和 API 约定在不同版本之间会发生变化。
首先,获取 https://supabase.com/changelog.md(轻量级摘要索引 —— 不是重量级拉取),扫描与您的任务相关的 breaking-change 标签,并遵循链接页面处理任何适用的更改。然后使用下面的文档访问方法查找相关主题。
2. 验证您的工作。 在实施任何修复后,运行测试查询以确认更改有效。没有验证的修复是不完整的。
3. 从错误中恢复,不要陷入循环。 如果一种方法在尝试 2-3 次后失败,请停下来重新考虑。尝试不同的方法,检查文档,更仔细地检查错误,并在可用时查看相关日志。Supabase 的问题并不总是通过重试相同的命令来解决,答案也不总是在日志中,但在继续之前检查日志通常是值得的。
4. 将表暴露给数据 API: 取决于用户的数据 API 设置,新创建的表可能不会自动通过数据(REST)API 暴露。如果是这种情况,则需要显式授予 anon 和 authenticated 角色访问权限。
请注意,这与 RLS 是分开的,RLS 控制的是表可访问后哪些_行_可见,而不是表是否根本可访问。
当用户报告 SQL 创建的表意外无法访问时,请检查他们的数据 API 设置以及是否通过显式 GRANT SQL 授予了角色访问权限。在授予公共(anon/authenticated)访问权限时,也要启用 RLS。有关完整设置工作流程,请参阅将表暴露给数据 API。
5. 暴露 Schema 中的 RLS。
在公开 Schema 中的每个表上启用 RLS,默认情况下包括 public Schema。这在 Supabase 中至关重要,因为当 anon/authenticated 角色具有访问权限时,公开 Schema 中的表可以通过数据 API 访问(请参阅将表暴露给数据 API)。对于私有 Schema,首选 RLS 作为深度防御。启用 RLS 后,创建与实际访问模型匹配的策略,而不是将每个表默认设置为相同的 auth.uid() 模式。
6. 安全检查清单。 在处理任何涉及 auth、RLS、视图、存储或用户数据的 Supabase 任务时,请运行此清单。以下是 Supabase 特定的安全陷阱,会静默创建漏洞:
-
Auth 和会话安全
- 切勿在基于 JWT 的授权决策中使用
user_metadata声明。 在 Supabase 中,raw_user_meta_data是用户可编辑的,并且可以出现在auth.jwt()中,因此它对 RLS 策略或任何其他授权逻辑来说是不安全的。而是将授权数据存储在raw_app_meta_data/app_metadata中。 - 删除用户不会使现有的访问令牌失效。 首先注销或撤销会话,为敏感应用程序保持较短的 JWT 过期时间,并且为了严格保证,在敏感操作上针对
auth.sessions验证session_id。 - 如果您使用
app_metadata或auth.jwt()进行授权,请记住 JWT 声明在用户令牌刷新之前并不总是最新的。
- 切勿在基于 JWT 的授权决策中使用
-
API 密钥和客户端暴露
- 切勿在公共客户端中暴露
service_role或密钥。 优先为前端代码使用可发布的密钥。旧的anon密钥仅用于兼容性。在 Next.js 中,任何NEXT_PUBLIC_环境变量都会被发送到浏览器。
- 切勿在公共客户端中暴露
-
RLS、视图和特权数据库代码
- 视图默认绕过 RLS。 在 Postgres 15 及更高版本中,使用
CREATE VIEW ... WITH (security_invoker = true)。在较旧版本的 Postgres 中,通过撤销anon和authenticated角色的访问权限,或将其放在未公开的 Schema 中来保护您的视图。 - UPDATE 需要 SELECT 策略。 在 Postgres RLS 中,UPDATE 需要首先 SELECT 该行。没有 SELECT 策略,更新将静默返回 0 行 —— 无错误,只是没有变化。
auth.role()已被弃用 —— 改用TO子句。 Supabase 已弃用auth.role(),转而直接在策略上使用TO authenticated或TO anon指定目标角色。除了弃用之外,当启用匿名登录时,auth.role() = 'authenticated'会静默失效,因为匿名用户携带authenticatedPostgres 角色,无论用户是否真正登录,都会通过检查。-- 已弃用(请勿使用) create policy "example" on table_name for select using ( auth.role() = 'authenticated' );TO authenticated单独使用是没有授权的身份验证(BOLA / IDOR)。 仅使用TO authenticated检查角色 —— 它不限制用户可以访问哪些行。正确的模式是将TO authenticated与USING中的所有权谓词结合起来:create policy "example" on table_name for select to authenticated using ( (select auth.uid()) = user_id );- UPDATE 策略需要同时包含
USING和WITH CHECK。 没有WITH CHECK,用户可以将行的user_id重新分配给另一个用户:create policy "example" on table_name for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id ); SECURITY DEFINER函数绕过 RLS。SECURITY DEFINER函数以其创建者的权限运行 —— 通常是具有bypassrls的角色(例如postgres)。切勿添加SECURITY DEFINER来解决权限错误;它会静默移除访问控制而不修复根本原因。优先使用SECURITY INVOKER。public中的SECURITY DEFINER函数可由所有角色调用。 Postgres 默认将每个新函数的EXECUTE授予PUBLIC,因此public中的任何SECURITY DEFINER函数都是一个公共 API 端点,可由anon和authenticated(它们继承自PUBLIC)调用,无需任何额外授权。当确实需要SECURITY DEFINER时(例如,绕过内部查找表上的 RLS),请将函数保留在未公开的 Schema 中,始终在函数体中包含auth.uid()检查,并在更改后运行supabase db advisors。
- 视图默认绕过 RLS。 在 Postgres 15 及更高版本中,使用
-
存储访问控制
- 存储 upsert 需要 INSERT + SELECT + UPDATE。 仅授予 INSERT 允许新上传,但文件替换 (upsert) 会静默失败。您需要所有三个权限。
-
依赖和供应链安全
- 安装 Supabase 软件包(
supabase-js、@supabase/ssr、supabase-py等)时,始终锁定软件包版本并提交锁定文件。 有关完整清单,请参阅 npm 安全指南。
- 安装 Supabase 软件包(
对于上述未涵盖的任何安全问题,请获取 Supabase 产品安全索引:https://supabase.com/docs/guides/security/product-security.md