Supabase 是做什麼的?
核心原則
1. Supabase 變更頻繁 — 在實現前務必對照變更日誌與目前文件進行驗證。 請勿僅依賴訓練資料中的 Supabase 功能。函式簽章、config.toml 設定與 API 慣例會隨著版本變更。
首先,取得 https://supabase.com/changelog.md(一個輕量級的摘要索引 — 並非大量拉取),掃描與你的任務相關的 breaking-change 標籤,並依照連結的頁面進行處理。然後使用下述的文件存取方法查閱相關主題。
2. 驗證你的工作。 在實施任何修正後,執行測試查詢以確認變更生效。未經驗證的修正是不完整的。
3. 從錯誤中恢復,不要反覆嘗試。 如果某個方法在嘗試 2-3 次後仍失敗,請停下來重新思考。嘗試不同方法、檢查文件、更仔細地檢查錯誤,並在可能時檢閱相關日誌。Supabase 的問題並非總是能透過重試相同指令解決,答案也不一定在日誌中,但在繼續前查閱日誌通常是值得的。
4. 將資料表暴露給 Data API: 根據使用者的 Data API 設定,新建立的資料表可能不會自動透過 Data (REST) API 暴露。如果發生此情況,則需要明確授予 anon 與 authenticated 角色存取權限。
請注意,這與 RLS 不同,RLS 控制的是資料表可存取後哪些資料列是可見的,而非資料表是否可存取。
當使用者回報某個透過 SQL 建立的資料表意外無法存取時,請檢查他們的 Data API 設定以及是否已透過明確的 GRANT SQL 授予角色存取權限。在授予公開(anon/authenticated)存取權限時,請務必同時啟用 RLS。請參閱將資料表暴露給 Data API 以了解完整的設定流程。
5. 暴露結構中的 RLS。
在任何暴露的結構(預設包含 public)中的每個資料表上啟用 RLS。這在 Supabase 中至關重要,因為當 anon/authenticated 角色擁有存取權限時(請參閱將資料表暴露給 Data API),暴露結構中的資料表可透過 Data API 存取。對於私有結構,建議將 RLS 作為深度防禦。啟用 RLS 後,請建立符合實際存取模型的原則,而不是將每個資料表都預設為相同的 auth.uid() 模式。
6. 安全檢查清單。 在處理任何涉及身分驗證、RLS、檢視表、儲存空間或用戶資料的 Supabase 任務時,請執行此檢查清單。這些是 Supabase 特定的安全陷阱,會悄悄產生漏洞:
-
身分驗證與工作階段安全
- 絕不要使用
user_metadata宣告作為基於 JWT 的授權決策依據。 在 Supabase 中,raw_user_meta_data是使用者可編輯的,且可能出現在auth.jwt()中,因此用於 RLS 原則或任何其他授權邏輯是不安全的。請改為將授權資料儲存在raw_app_meta_data/app_metadata中。 - 刪除使用者並不會使現有的存取令牌失效。 請先登出或撤銷工作階段,對於敏感應用程式請保持 JWT 有效期較短,若需嚴格保證,請在敏感操作中根據
auth.sessions驗證session_id。 - 如果你使用
app_metadata或auth.jwt()進行授權,請記住 JWT 宣告在使用者的令牌刷新前不一定是最新的。
- 絕不要使用
-
API 金鑰與用戶端暴露
- 絕不要在前端用戶端暴露
service_role或秘密金鑰。 前端程式碼首選使用公開金鑰。舊版anon金鑰僅用於相容性。在 Next.js 中,任何NEXT_PUBLIC_環境變數都會被發送到瀏覽器。
- 絕不要在前端用戶端暴露
-
RLS、檢視表與特權資料庫程式碼
- 檢視表預設會繞過 RLS。 在 Postgres 15 及以上版本中,請使用
CREATE VIEW ... WITH (security_invoker = true)。在較舊版本的 Postgres 中,請透過撤銷anon與authenticated角色的存取權限,或將檢視表放在未暴露的結構中來保護你的檢視表。 - 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 預設為每個新函式授予PUBLICEXECUTE權限,因此public中的任何SECURITY DEFINER函式都是一個公開的 API 端點,可由anon與authenticated(它們繼承自PUBLIC)呼叫,無需任何額外授權。當確實需要SECURITY DEFINER時(例如,繞過內部查閱資料表的 RLS),請將該函式保留在未暴露的結構中,務必在函式主體中包含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