Supabase RLS 策略审查 Prompt:鉴权与角色模板

Supabase 行级安全 Prompt——auth.uid() 检查、角色策略、INSERT/UPDATE/DELETE 覆盖、Storage 桶 RLS、函数安全。

大多数”审 RLS”Prompt 只会让你开启 RLS。真正的 RLS bug 更隐蔽:UPDATE 漏 WITH CHECK、service_role 通过函数泄漏、auth.uid() 比错了列。下面 15 个 Prompt 各打一类具体 RLS 失败。

适合哪些场景

发 Supabase 应用的独立创业者、审 schema PR 的 reviewer、准备渗透测试材料的安全工程师、公开上线前的独立开发者。

什么时候不建议这样写 Prompt

非 Supabase Postgres 别用(auth.uid() 和 Storage 部分不适用)。故意关 RLS 的原型也别用。

Prompt 结构公式

每个 RLS 审查 Prompt 都要带这六个要素:

  • 角色:让 AI 扮演谁(架构师 / SRE / QA Lead / Release Captain)。
  • 上下文:仓库 / 框架 / 运行时版本 / 涉及哪些文件或 diff。
  • 目标:一个具体可交付物——review 笔记、计划、checklist、测试文件、交接文档。
  • 限制:AI 不能做什么(别重写、别自动 format、别瞎猜版本号)。
  • 输出格式:编号清单、markdown 表格、JSON、unified diff、可直接运行的代码。
  • 示例 / 信号:1-2 条”好输出”示例,或者说明什么是糟糕输出。

这套 Prompt 适合用在哪

  • 上线前 RLS 审计
  • Schema migration PR review
  • 事故后策略硬化
  • 角色 / service_role 使用审计
  • Storage 桶访问审查

15 个可直接复制的 Prompt 模板

1. RLS 启用覆盖检查

第一个跑——不开 RLS,后面所有策略都无效。

You are a Supabase security reviewer. Audit the schema dump below. For every table in `public`: (1) Is RLS enabled? (2) Does it have at least one policy? (3) Is FORCE ROW LEVEL SECURITY enabled (relevant for table owner access)? Output a table: schema.table | RLS enabled | policy count | force RLS | risk.

2. auth.uid() 比较正确性

Review every policy USING / WITH CHECK clause that references `auth.uid()`. For each: (1) Is it compared against the column that actually holds the owner (e.g., user_id, owner_id, profile_id)? (2) Could the comparison silently pass for nulls? (3) Are joined tables also constrained, or is the JOIN unprotected? List findings: policy name | issue | fix sketch.

3. INSERT / UPDATE / DELETE 覆盖

For each table with RLS enabled, list which commands have policies: SELECT, INSERT, UPDATE, DELETE. Flag tables missing policies for any command — the default is deny, but partial policies often leave gaps (e.g., UPDATE without WITH CHECK lets users move rows to other owners). Output as a matrix.

4. WITH CHECK vs USING 审计

Review every UPDATE and INSERT policy. For each: (1) Does it have BOTH USING (for the pre-image) and WITH CHECK (for the post-image)? (2) On UPDATE, does WITH CHECK prevent reassigning ownership to another user? (3) On INSERT, does WITH CHECK prevent inserting on behalf of another user? List violations with file:line.

5. service_role 绕过审计

Find every usage of `service_role` key in the codebase (server functions, edge functions, migrations, background jobs). For each: (1) Is it strictly necessary, or could `authenticated` work? (2) Is the call wrapped in a function that re-checks ownership? (3) Are any service_role queries reachable from user input without validation? File:line + severity.

6. Storage 桶 RLS 审查

Audit Supabase Storage bucket policies. For each bucket: (1) public vs private flag, (2) SELECT/INSERT/UPDATE/DELETE policies on `storage.objects` filtered to this bucket, (3) Does the path convention encode ownership (e.g., `{user_id}/...`) and does the policy enforce that prefix? (4) Are signed URLs used where private buckets need temp access? Output: bucket | policies | risks.

7. SECURITY DEFINER 函数审计

List every Postgres function marked SECURITY DEFINER (including those Supabase generates). For each: (1) Does it set `search_path` explicitly to prevent search_path attacks? (2) Does it re-validate auth.uid() if it bypasses RLS? (3) Is the function callable by anon? (4) Is grant correct? Findings with severity.

8. Realtime 订阅策略审计

Audit Supabase Realtime publication and policies: (1) Which tables are in `supabase_realtime` publication? (2) Do those tables have SELECT policies that constrain rows visible to a user? (Realtime respects RLS — if SELECT is loose, broadcasts leak.) (3) Are any sensitive columns published when they shouldn't be? List risks.

9. 多租户隔离 review

This app is multi-tenant via `tenant_id` (or {tenantColumn}). Audit: (1) Every tenant-scoped table has tenant_id and an RLS policy comparing it to the user's tenant claim, (2) JWT claim extraction (`auth.jwt() ->> 'tenant_id'` or similar) is consistent, (3) Cross-tenant reads via JOINs are blocked. Output: table | tenant policy | leak risk.

可替换变量: tenantColumn —— 例如 tenant_id, org_id, workspace_id

10. 角色策略审计

If this schema uses role columns (admin, member, viewer), audit role policies: (1) Are roles read from a profile table or a JWT claim? Pick one — mixing is a footgun, (2) Do admin-only mutations check role inside the policy, or only in the app? (3) Is there a way to escalate role via UPDATE on profiles? List findings.

11. Migration 策略 diff review

migration PR 上用。

Below is a migration diff. For RLS changes: (1) Any policy dropped without replacement? (2) Any new table missing RLS? (3) Any column type change that breaks an existing policy comparison? (4) Are policies idempotent (CREATE OR REPLACE / DROP IF EXISTS)? Output PR-ready review comments.

可替换变量: migration diff 文本

12. anon vs authenticated 拆分 review

For every policy, classify which role it applies to (anon, authenticated, service_role). Flag: (1) policies that apply to anon when they should be authenticated only, (2) policies that apply to authenticated when only specific roles should access, (3) policies missing role filter (default = all roles).

13. 策略性能 review

Review RLS policies for performance: (1) Policies that call functions per-row instead of inlining the auth check, (2) Policies that JOIN large tables — these become per-row subqueries, (3) Missing index on the column compared to auth.uid(), (4) `auth.uid()` invoked multiple times per policy instead of cached. Suggest specific indexes and rewrites.

14. RLS 漏洞 → 红队场景

Take the policies below and write 5 red-team scenarios: each is a JWT + a SQL query + the expected (denied) result. Then say which of the 5 would actually succeed against current policies and why. Use this as a regression-test seed.

15. RLS 发现 → 修复计划

最后跑——把发现转成 migration 序列。

Take all RLS findings above and group them into ordered migrations. For each migration: (1) Title, (2) SQL diff (DROP / CREATE / ALTER), (3) Pre-deploy check (count rows that would now be denied — sanity check), (4) Rollback. Mark which migrations need maintenance windows.

容易踩的坑

  • 只看 USING 不看 WITH CHECK——UPDATE / INSERT 泄漏悄悄过。
  • “内部代码”无脑信 service_role 不重新检查所有权——每条 service_role 路径都是特权面。
  • auth.uid() 比错列(id 而不是 user_id)——零行结果掩盖了 bug。
  • 开了 RLS 但没 FORCE ROW LEVEL SECURITY——表 owner 查询绕过策略。
  • Realtime 发布的表 SELECT 策略松——泄漏面成倍放大。
  • 混用角色查询来源(profile 表 vs JWT)——改角色时有竞态。
  • RLS PR 跳过 migration review——被删的策略看起来是”加了安全”,其实是回归。

优化技巧

  • 永远先跑模板 1(启用覆盖)——RLS 没开,其他审计都没意义。
  • 强制 reviewer 写一个红队场景(模板 14),能挖出静态 review 漏的。
  • 多租户应用,所有策略的 auth.jwt() claim 提取要一致。
  • WITH 子句或函数把 auth.uid() 缓存,性能更好。
  • 配合 service_role 审计跑——大多数泄漏在那里不在策略文本。
  • 每次 migration PR 后重跑审计——RLS 状态会悄悄漂移。
  • 把策略快照存到 /supabase/policies.snapshot.sql,diff over time。

FAQ

  • 不运行怎么测策略?: 静态 review 覆盖结构。运行时用 Supabase 的 select set_config('request.jwt.claims', ...) 在 psql 模拟用户。
  • AI 能直接生成策略吗?: 能出初稿,但生成的策略一定要跑这套 review——常漏 WITH CHECK 和角色过滤。
  • 应用做了鉴权还要 RLS 吗?: 要。应用层只要有人绕到 DB 直连(PostgREST、SQL editor、anon key 泄漏),鉴权就失守。RLS 是最后一道。
  • 开 RLS 后 SELECT 零行?: 默认 deny。至少要一个匹配该用户行的 SELECT 策略。跑模板 3 找缺策略。
  • service_role 调用都要重新检查所有权吗?: 只要接收用户提供的标识,就要。重新检查或用 SECURITY INVOKER 函数包一层。
  • 怎么防止 UPDATE profiles 提权?: 加列级 check 或 trigger 拒绝 authenticated 用户改 role 字段。模板 10 覆盖了。

相关阅读

标签: #Prompt #编程 #Supabase #安全