Row-Level Security (RLS)
部分內容由 LLM 生成,尚未經過人工驗證。
什麼是 RLS
Row-Level Security(列層級安全性)是 PostgreSQL 內建的資料存取控制機制,能在資料庫層自動過濾資料列,確保使用者只能存取被授權的資料。
適用場景:
- 多租戶 SaaS 應用
- 敏感資料隔離(如醫療、金融)
- 需要強制資料隔離的合規需求
傳統設計 vs RLS
| 方案 | 做法 | 缺點 |
|---|---|---|
| 應用層過濾 | 每個查詢加 WHERE tenant_id = ? | 容易遺漏、難以稽核 |
| View + GRANT | 建立過濾 View 並授權 | 維護成本高、View 數量爆炸 |
| 分離 Schema/DB | 每租戶獨立 Schema 或資料庫 | 擴展困難、連線管理複雜 |
| RLS | Policy 自動過濾 | 需 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 執行計畫,避免全表掃描