Virtual Private Database (VPD),即細粒度存取控制 (Fine-Grained Access Control, FGAC),是 Oracle 企業級安全的核心:以行級安全策略,在查詢重寫階段透明附加謂詞,實現「同一張表、不同使用者看到不同資料」,零應用程式修改。10g 引入四種新策略類型,配合應用程式上下文,提供從「每行每次重算」到「SGA 快取重用」的效能光譜。本文系統解析 VPD 架構、五大策略類型、適用場景與陷阱避坑。
schema, table,回傳 VARCHAR2 謂詞片段 (如 dept_id = SYS_CONTEXT('ctx', 'dept'))。DBMS_RLS.ADD_POLICY 綁定函數至物件,指定 POLICY_TYPE。SELECT * FROM accounts → Oracle 內部重寫為 SELECT * FROM accounts WHERE <謂詞>。| 策略類型 | 參數值 | 重新評估頻率 | 謂詞特徵 | 適用場景 | 效能 |
|---|---|---|---|---|---|
| Dynamic (預設、9i 相容) | DBMS_RLS.DYNAMIC |
每次存取、每行、每使用者 | 高度動態,依賴 Session/User/系統狀態 | 複雜權限邏輯、即時變動需求 | 最低 (CPU 高) |
| Context Sensitive | DBMS_RLS.CONTEXT_SENSITIVE |
應用程式上下文變更時 | 謂詞結構固定,僅參數值動態 (如 balance < SYS_CONTEXT('ctx','max')) |
Web 應用共用 DB User、上下文驅動權限 | 中 (避免重複評估) |
| Shared Context Sensitive | DBMS_RLS.SHARED_CONTEXT_SENSITIVE |
跨會話共享上下文變更時 | 同上,但謂詞在 SGA 共享池快取,多會話重用 | 大量併發會話、相同上下文值 | 中高 |
| Static | DBMS_RLS.STATIC |
僅第一次載入 (SGA 快取永久) | 完全靜態,謂詞不含 Session 變數 (如 cust_name = 'ACME') |
托管環境、固定租戶隔離、登入觸發器設定上下文 | 最高 (近零開銷) |
| Shared Static | DBMS_RLS.SHARED_STATIC |
跨會話共享,永久快取 | 同上,多會話共享同一謂詞快取 | 大規模 SaaS、固定分區策略 | 最高 |
CREATE OR REPLACE FUNCTION vpd_dynamic_f (p_schema VARCHAR2, p_table VARCHAR2)
RETURN VARCHAR2 AS
BEGIN
RETURN 'dept_id = ' || SYS_CONTEXT('USERENV', 'SESSION_USER'); -- 每次皆算
END;
/
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'SCOTT',
object_name => 'EMP',
policy_name => 'EMP_DYNAMIC',
function_schema => 'SEC_ADMIN',
policy_function => 'VPD_DYNAMIC_F',
policy_type => DBMS_RLS.DYNAMIC -- 可省略,預設即此
);
END;
/
-- 應用上下文:由中介層登入後 SET CONTEXT
CREATE OR REPLACE CONTEXT vpd_ctx USING vpd_pkg;
CREATE OR REPLACE FUNCTION vpd_cs_f (p_schema VARCHAR2, p_table VARCHAR2)
RETURN VARCHAR2 AS
BEGIN
RETURN 'balance < SYS_CONTEXT(''VPD_CTX'', ''MAX_BAL'')'; -- 結構固定,參數來自上下文
END;
/
BEGIN
DBMS_RLS.ADD_POLICY(
policy_type => DBMS_RLS.CONTEXT_SENSITIVE, -- 關鍵差異
...);
END;
/
-- 效能機制:若會話期間 VPD_CTX.MAX_BAL 未變,Oracle 跳過函數執行,直接重用謂詞
CREATE OR REPLACE FUNCTION vpd_static_f (p_schema VARCHAR2, p_table VARCHAR2)
RETURN VARCHAR2 AS
BEGIN
RETURN 'cust_id = SYS_CONTEXT(''VPD_CTX'', ''CUST_ID'')'; -- 謂詞結構+值皆固定
END;
/
BEGIN
DBMS_RLS.ADD_POLICY(
policy_type => DBMS_RLS.STATIC, -- 只執行一次,結果快取於 SGA
...);
END;
/
-- ⚠️ 風險:若會話中 CUST_ID 變更,謂詞不會更新 → 資料洩露/錯誤隔離
-- 必須保證:登入後上下文不可變,或登出重登
SHARED_* 類型將謂詞快取於共享池 (Shared Pool),而非私有 PGA。適合大量會話共用相同上下文值 (如多租戶 SaaS),減少記憶體佔用與 CPU。需設定 SHARED_CONTEXT_SENSITIVE 時指定 NAMESPACE 參數。
DBMS_RLS.CREATE_POLICY_GROUP 將多個策略分組,應用程式透過 DBMS_RLS.ADD_GROUPED_POLICY 動態啟用/停用群組,實現多層級安全 (如:基礎隔離組 + 業務規則組 + 合規審計組)。
DBMS_RLS.ADD_POLICY(..., SEC_RELEVANT_COLS => 'SALARY', SEC_RELEVANT_COLS_OPT => DBMS_RLS.ALL_ROWS):僅當查詢涉及敏感欄位 (如薪資) 時套用謂詞,其他查詢不受限。EXEMPT ACCESS POLICY 系統權限可讓特定使用者 (如 DBA、批次帳號) 繞過所有 VPD。
DBMS_SESSION.SET_CONTEXT。V$VPD_POLICY、V$RLS_POLICY 觀察策略命中與評估次數。VPD 以「查詢重寫」實現零侵入行級安全。策略類型選擇即效能架構決策:Static > Shared Static > Shared Context Sensitive > Context Sensitive > Dynamic。準確識別業務的「權限變動頻率與範圍」,選對類型、設計好上下文、配合索引,即可在安全合規與高併發效能間取得最佳平衡。