← 課程地圖

SUPABASE CORE · 117

Row Level Security:把「誰能碰哪一列」放進 Database

前端 hidden button 不是 authorization;API filter 也可能漏。RLS 讓 PostgreSQL 在資料層依 authenticated context 對每筆 row 執行 policy。

Learning outcomes

1. Ownership model

courses
  id
  owner_id
  title
  score

Policy idea:
  owner_id = auth.uid()

2. SELECT:existing rows 可見性

USING (
  owner_id = auth.uid()
)

概念上,USING 限制 operation 能作用/看見哪些 existing rows。

3. INSERT / UPDATE new row

WITH CHECK (
  owner_id = auth.uid()
)

不能只限制 user A 看到自己資料,卻讓他 insert owner_id=user-B。

4. Operation-specific policy

Operation問題
SELECT哪些 rows 可見?
INSERT新 row 合法嗎?
UPDATE原 row 可改、改後仍合法嗎?
DELETE能刪哪一列?

5. RLS ≠ Constraint

RLS

user A 可不可以碰 row X?

CHECK / FK / UNIQUE

row X 本身是否合法?

Project checkpoint:Course Workspace v5

User A:
  SELECT A rows      PASS
  SELECT B rows      DENY
  INSERT owner=A     PASS
  INSERT owner=B     DENY
  UPDATE A row       PASS
  DELETE B row       DENY

Anon:
  private rows       DENY

6. Policy 生效時,不一定會直接看到「Permission denied」

對某些 SELECT 情境,RLS 的效果可能表現成「查不到不允許看見的 rows」,而不是把每次查詢都轉成明顯 403。這代表 data=[] 本身不足以證明 table 真的沒有資料。

已知 database 有:
A row → owner_id = user-A
B row → owner_id = user-B

目前 session = user-A

SELECT courses
可能只得到:
[A row]

因此看到空結果時,要同時確認 authenticated identity、query filter、資料是否存在,以及 SELECT policy,而不是立即判定 Database 壞掉。

7. Debug evidence:用不同 identity 重播同一個 operation

Privileged context 可能 bypass 一般 policies,所以只用 admin 測試會掩蓋真實 user policy。最有價值的 evidence 是固定同一筆資料、同一個 query,只改 request 的 identity。

anon   → expected DENY
user A → expected own rows
user B → expected B rows
admin  → only where explicitly intended

如果 user A 能看到 B row,就往 policy expression / authenticated context 查;如果 admin 正常但 user A 全空,不代表 query syntax 一定錯,可能是 policy 根本沒允許一般 user。

8. Current RLS security traps

TO authenticated 不等於 ownership authorization

FOR SELECT
TO authenticated
USING (true)

這只代表「已登入角色可以進來」,沒有回答 user A 能不能讀 user B 的 row。Ownership model 仍要明確寫進 USING。

UPDATE 要同時想 existing row 與 new row

USING (
  auth.uid() = user_id
)
WITH CHECK (
  auth.uid() = user_id
)

USING 控制目前哪些 rows 可被 update;WITH CHECK 控制 update 後的新 row value 是否仍合法。缺少後者可能讓 owner 欄位被改成別人。

不要用 user-editable metadata 做 privileged authorization

user_metadata 可由 user 修改,因此不應拿來判斷 admin 等高權限角色。需要 JWT-based authorization metadata 時,應使用 server-controlled / app metadata 或其他可信來源。

Knowledge check

  1. Frontend owner filter 為何不能取代 RLS?
  2. USING / WITH CHECK 各關心什麼?
  3. 設計 A/B CRUD test matrix。
  4. score 0–100 應用 RLS 還是 CHECK?