Row Level Security
Row Level Security(列級安全性, RLS)是 Postgres 的功能:即使請求通過了 API key,每一列還要再通過政策(policy)才看得到。Supabase 把 publishable key 公開給前端,所以 RLS 不是選配,是預設防線。
沒開 RLS 的表,REST API 等同公開。
先啟用,再寫政策
只啟用、不寫任何政策時,anon 與 authenticated 什麼都做不了(結果是空的或寫入被拒)。這是安全的失敗模式。接著依需求加政策。
官方建議把 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。
除錯順序
- 表有沒有
enable row level security? - 欄位
user_id型別是不是uuid,值是不是真的等於auth.uid()? - 前端有沒有成功
signIn?未登入時角色是anon,對不到authenticated政策。 - 你是不是誤用了 secret key?那會跳過政策,開發時「都通了」、換成 publishable key 就全掛。
- GRANT:Postgres 先看表權限再看 RLS。缺少 GRANT 會直接 permission denied,看起來不像政策問題。
Dashboard 的 Security Advisor 會列出常見疏漏(表沒開 RLS、政策過寬)。上線前掃一次。
相關資料
- 官方:Row Level Security
- 金鑰與角色對照:API Keys
- Storage 的檔案權限也是 RLS,見 Storage