Row-Level Security (RLS)

部分內容由 LLM 生成,尚未經過人工驗證。

什麼是 RLS

Row-Level Security(列層級安全性)是 PostgreSQL 內建的資料存取控制機制,能在資料庫層自動過濾資料列,確保使用者只能存取被授權的資料。

適用場景:

  • 多租戶 SaaS 應用
  • 敏感資料隔離(如醫療、金融)
  • 需要強制資料隔離的合規需求

傳統設計 vs RLS

方案做法缺點
應用層過濾每個查詢加 WHERE tenant_id = ?容易遺漏、難以稽核
View + GRANT建立過濾 View 並授權維護成本高、View 數量爆炸
分離 Schema/DB每租戶獨立 Schema 或資料庫擴展困難、連線管理複雜
RLSPolicy 自動過濾需 PostgreSQL 9.5+

核心語法

啟用 RLS

-- 啟用 RLS(僅對非 owner 生效)
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

-- 強制 owner 也受 RLS 限制
ALTER TABLE orders FORCE ROW LEVEL SECURITY;

建立策略 (Policy)

CREATE POLICY policy_name ON table_name
    [AS { PERMISSIVE | RESTRICTIVE }]
    [FOR { ALL | SELECT | INSERT | UPDATE | DELETE }]
    [TO { role_name | PUBLIC | CURRENT_ROLE | CURRENT_USER | SESSION_USER }]
    USING (condition)           -- 過濾現有資料列
    [WITH CHECK (condition)];   -- 驗證新增/修改資料列

策略範例(多租戶)

-- 建立租戶表
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    tenant_id INT NOT NULL,
    product TEXT,
    amount DECIMAL
);

-- 啟用 RLS
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

-- 建立隔離策略:用戶只能存取自己租戶的資料
CREATE POLICY tenant_isolation ON orders
    FOR ALL
    TO PUBLIC
    USING (tenant_id = current_setting('app.tenant_id')::INT)
    WITH CHECK (tenant_id = current_setting('app.tenant_id')::INT);

應用層設定租戶 ID:

-- 每次連線或 transaction 開始時設定
SET app.tenant_id = '42';

-- 之後的查詢自動過濾
SELECT * FROM orders;  -- 只返回 tenant_id = 42 的資料

RLS 特性

特性說明
透明性查詢無需修改,Policy 自動套用
多 Policy同表可有多個 Policy,預設 OR 組合(PERMISSIVE)
操作區分可針對 SELECT/INSERT/UPDATE/DELETE 分別設定
角色指定Policy 可指定適用的資料庫角色
Bypass 權限BYPASSRLS 角色屬性可跳過所有 RLS

實務建議

多租戶 SaaS

  • 使用 session variable(如 app.tenant_id)傳遞租戶識別
  • 在連線池層設定,確保每個請求正確設定 tenant_id
  • 考慮使用 FORCE ROW LEVEL SECURITY 防止誤操作

敏感資料隔離

  • 結合資料庫角色與 RLS,實現細粒度權限
  • 定期稽核 Policy 設定

測試注意事項

  • 測試時切換不同 tenant_id 驗證隔離效果
  • 注意 superuser 和 table owner 預設繞過 RLS
  • 使用 EXPLAIN 確認 Policy 是否正確套用

效能考量

  • Policy 條件應加上適當索引(如 tenant_id
  • 複雜 Policy 可能影響查詢效能,需實測
  • 監控 Policy 執行計畫,避免全表掃描