當你把資料庫的鑰匙交給了前端網頁
在傳統的軟體架構中,資料庫就像是一個深藏在地下室的保險箱。前端的網頁(瀏覽器)是絕對無法直接碰到這個保險箱的。網頁必須要透過「後端伺服器 (如 Node.js 或 Python)」作為警衛,由警衛檢查使用者的證件後,警衛再親自下樓去保險箱拿資料。
但隨著雲端技術與 Supabase 的崛起,現在流行一種名為 「無後端 (Serverless / Backend-as-a-Service)」 的開發架構。
在第二章中,我們教你把 Supabase 的 anon_key 直接放進了 .env.local 裡,讓 Next.js / React 前端網頁直接連線到資料庫抓資料。
這帶來了前所未有的開發速度,但同時也伴隨著一個巨大的資安漏洞:萬一有心人士在網頁上按了 F12 開發者工具,把你的 anon_key 偷走怎麼辦?
如果他拿著這把鑰匙,寫了一段小腳本,對你的資料庫下達了 DELETE FROM users(刪除所有會員)的指令,你的心血不就瞬間歸零了?
為了解決這個致命問題,PostgreSQL 祭出了它最引以為傲的終極防禦武器:RLS (Row-Level Security,行級安全性)。
什麼是 RLS?為每一行資料派駐一位隨身保鑣
傳統的資料庫權限,就像是「大門門禁」。一旦駭客突破了大門進入了 orders (訂單) 表格,他就可以看見所有人的訂單。
而 RLS (Row-Level Security) 的概念完全不同。它就像是在表格裡的「每一行 (Row)」旁邊,都站著一位荷槍實彈的保鑣。
當駭客拿著 anon_key 試圖想要拿走王大明的訂單時,保鑣會立刻攔住他:
「請出示你的 JWT 通行證!你必須證明你是『王大明本人』,我才允許你把這行資料拿走。否則,你在這張表裡什麼都看不到!」
有了 RLS 的保護,即使你的 anon_key 不小心外洩,駭客也無能為力。因為 anon_key 只是讓他能「敲門」,而 RLS 才會決定「門後有什麼東西」願意顯示給他看。
RLS 的核心心法:Policy (政策)
在 Supabase 中,你要如何訓練這些保鑣呢?你需要為每個表格寫下名為 Policy (政策) 的法律條文。
政策通常包含兩個要素:
- 能做什麼事 (Action):例如
SELECT(讀取)、INSERT(新增)、UPDATE(修改)、DELETE(刪除)。 - 什麼條件下允許 (USING / WITH CHECK):例如「當目前登入者的 ID 等於這行資料的 user_id 時」。
常見的 RLS 商業情境設定
情境一:公開的商品目錄 (所有人都能看)
對於 products (商品表),我們希望不管是登入的會員,還是路過的訪客,都可以看到商品資訊。但是不允許任何人修改或刪除商品。
- 動作:允許
SELECT。 - 條件:
true(無條件允許所有人)。 - (附註:因為我們沒有寫
INSERT或UPDATE的 Policy,所以任何試圖修改商品的動作都會被保鑣直接擋下!)
情境二:私人的購物車與訂單 (只有自己能看自己的)
對於 orders (訂單表),這是極度隱私的資料。王大明只能看到王大明的訂單,絕對不能看到陳小美買了什麼。
- 動作:允許
ALL(包含查詢、新增、修改、刪除)。 - 條件:這行資料的
user_id必須等於 目前登入使用者的 UID (auth.uid())。
實戰:用 Vibe Prompt 秒解 RLS 設定難題
RLS 雖然強大,但它的 SQL 語法非常冗長且容易寫錯。在 Vibe Coding 中,你絕對不應該自己手寫 RLS 政策。
當你在 Supabase 開發時,如果遇到「資料明明有存進去,但是前端怎麼撈都是一片空白」的情況,恭喜你,你 100% 撞到 RLS 防火牆了。這時候,請開啟 Cursor 的 SQL Editor,對著 AI 下達這段終極解鎖咒語:
【Supabase RLS 解鎖 Prompt】 我正在使用 Supabase。我有一個名為
user_profiles的表格,欄位有id(關聯到 auth.users),nickname,avatar_url。 目前因為 RLS 的關係,我的前端網頁無法讀取或更新資料。 請幫我寫一段完整的 SQL 腳本,完成以下 RLS 政策設定:
- 開啟
user_profiles的 RLS 功能 (ENABLE ROW LEVEL SECURITY)。- 建立第一條 Policy:允許「任何人 (包含匿名訪客)」讀取 (
SELECT) 所有人的資料。- 建立第二條 Policy:只允許「使用者本人」可以修改 (
UPDATE) 或新增 (INSERT) 屬於自己的資料 (條件是 auth.uid() 等於這行資料的 id)。 請確保語法完美相容於 Supabase 的 PostgreSQL 環境。
AI 產出的神級保鑣訓練腳本:
-- 1. 喚醒表格的保鑣系統 (開啟 RLS)
ALTER TABLE user_profiles ENABLE ROW LEVEL SECURITY;
-- 2. 政策一:所有人都可以看 (SELECT)
CREATE POLICY "Public profiles are viewable by everyone"
ON user_profiles
FOR SELECT
USING (true);
-- 3. 政策二:只有本人可以新增 (INSERT)
CREATE POLICY "Users can insert their own profile"
ON user_profiles
FOR INSERT
WITH CHECK (auth.uid() = id);
-- 4. 政策三:只有本人可以修改 (UPDATE)
CREATE POLICY "Users can update own profile"
ON user_profiles
FOR UPDATE
USING (auth.uid() = id);
你只需要把這段腳本複製,貼到 Supabase 的 SQL 控制台按下執行。 瞬間,你的資料庫就獲得了軍用級別的安全防護。你可以安心地把你寫好的網頁交給客戶,再也不用擔心駭客把資料庫搞砸了!
恭喜您!完成本章節後,您已經完全掌握了現代資料庫從「架設、關聯運算、加密、到最高級的 RLS 資安防護」的完整生態系。身為一個強大的 Vibe Coder,世界上的所有數據現在都任您差遣了!
更多 RLS 應用場景
多租戶應用
-- 每個使用者只能看到自己的資料
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::INT);
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
唯讀管理員
-- 管理員可以讀取所有資料,但只能寫入日誌表
CREATE POLICY admin_read_all ON products FOR SELECT
USING (true);
CREATE POLICY admin_write_logs ON audit_logs FOR INSERT
WITH CHECK (true);
效能注意事項
- RLS 會增加每次查詢的開銷,特別是在複雜的政策中
- 對於頻繁存取的表格,建議使用索引來加速政策評估
- 在高吞吐量的系統中,考慮使用應用層授權代替 RLS
Row Level Security:列級安全防護
什麼是 RLS?
RLS(Row Level Security)讓你可以設定「每一列的存取權限」——使用者 SELECT 時只會看到自己有權限的資料。這在多租戶(Multi-tenant)應用中非常有用。
RLS 的運作方式
-- 啟用 RLS
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- 建立政策:使用者只能看自己的訂單
CREATE POLICY user_orders ON orders
FOR SELECT
USING (user_id = current_setting('app.user_id')::uuid);
-- 管理員可以看所有訂單
CREATE POLICY admin_all_orders ON orders
FOR ALL
USING (current_setting('app.role') = 'admin');
為什麼用 RLS 而不是應用程式過濾?
在應用程式中用 WHERE user_id = ? 過濾雖然也能達到同樣效果,但 RLS 是在資料庫層級強制執行的——即使有人直接連線資料庫(透過 SQL Editor),也無法繞過 RLS 看到別人的資料。這是 Defense in Depth 的重要一環。
下一章預告:Supabase Webhook
安全防護建立好之後,下一章教你用 Supabase Webhook 串接外部服務。