Skip to content

建立 2026-09-14 更新 2026-09-14

Row Level Security

Row Level Security(列級安全性, RLS)是 Postgres 的功能:即使請求通過了 API key,每一列還要再通過政策(policy)才看得到。Supabase 把 publishable key 公開給前端,所以 RLS 不是選配,是預設防線

沒開 RLS 的表,REST API 等同公開。

先啟用,再寫政策

alter table todos enable row level security;

只啟用、不寫任何政策時,anonauthenticated 什麼都做不了(結果是空的或寫入被拒)。這是安全的失敗模式。接著依需求加政策。

官方建議把 auth.uid() 包在子查詢裡,讓 Postgres 每句只算一次:

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

create policy "Users can insert own todos"
on todos
for insert
to authenticated
with check ( (select auth.uid()) = user_id );

create policy "Users can update own todos"
on todos
for update
to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );

create policy "Users can delete own todos"
on todos
for delete
to authenticated
using ( (select auth.uid()) = user_id );

using 決定「哪些現有列適用」;with check 決定「寫入後的新列是否合法」。Update 兩個都要,否則使用者可能把 user_id 改成別人。

anon 與 authenticated

角色 何時出現 典型政策
anon 未登入,只用 publishable key 公開讀取,例如部落格文章 for select to anon
authenticated 已登入 只能碰 user_id = auth.uid() 的列
service_role secret key 略過 RLS,不要為它寫政策

需要「大家都能看、只有作者能改」時,給 anon(或 authenticated)一條 select,再給作者 insert/update/delete。

除錯順序

  1. 表有沒有 enable row level security
  2. 欄位 user_id 型別是不是 uuid,值是不是真的等於 auth.uid()
  3. 前端有沒有成功 signIn?未登入時角色是 anon,對不到 authenticated 政策。
  4. 你是不是誤用了 secret key?那會跳過政策,開發時「都通了」、換成 publishable key 就全掛。
  5. GRANT:Postgres 先看表權限再看 RLS。缺少 GRANT 會直接 permission denied,看起來不像政策問題。

Dashboard 的 Security Advisor 會列出常見疏漏(表沒開 RLS、政策過寬)。上線前掃一次。

相關資料