Virtual Private Database
Virtual Private Database とは
Section titled “Virtual Private Database とは”Virtual Private Database(VPD)は、DBMS_RLS パッケージを使ってテーブル・ビューにポリシーを設定し、SQL 実行時に自動的に WHERE 句を付加することで行・列レベルのアクセス制御を実現する機能です。
アプリケーション側のコードを変更せず、データベース層で透過的にアクセス制御できます。同じ SQL でもユーザーやセッション属性によって返される結果が変わります。
行制御と列制御
Section titled “行制御と列制御”行レベル制御
Section titled “行レベル制御”ポリシー関数が返す述語を WHERE 句に付加することで、参照できる行を制限します。
-- ポリシー関数の例(セッションユーザーで絞り込み条件を切り替え)CREATE OR REPLACE FUNCTION hr.get_sales_predicate( p_schema IN VARCHAR2, p_table IN VARCHAR2) RETURN VARCHAR2 ISBEGIN IF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'SALES_APP' THEN RETURN 'JOB_ID LIKE ''SA_%'''; -- 営業系のみ ELSE RETURN '1=1'; -- 全件 END IF;END;列レベル制御
Section titled “列レベル制御”DBMS_RLS.ADD_POLICY の sec_relevant_cols パラメータで対象列を指定します。指定列がクエリに含まれたときだけポリシーが発動します。
sec_relevant_cols_opt | 動作 |
|---|---|
| 未指定(デフォルト) | 条件を満たさない行を除外(0件になる場合あり) |
DBMS_RLS.ALL_ROWS | 条件を満たさない行の対象列を NULL で表示 |
BEGIN DBMS_RLS.ADD_POLICY ( object_schema => 'HR', object_name => 'EMPLOYEES', policy_name => 'employees_salary_col_vpd_policy', function_schema => 'HR', policy_function => 'get_masking_salary_col', sec_relevant_cols => 'SALARY', sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS -- NULL表示 );END;主要プロシージャ
Section titled “主要プロシージャ”| プロシージャ | 説明 |
|---|---|
DBMS_RLS.ADD_POLICY | テーブル・ビューにポリシーを追加する |
DBMS_RLS.DROP_POLICY | ポリシーを削除する |
DBMS_RLS.ENABLE_POLICY | ポリシーを有効化・無効化する |
ADD_POLICY の主なパラメータ
Section titled “ADD_POLICY の主なパラメータ”| パラメータ | 説明 |
|---|---|
object_schema / object_name | ポリシーを適用するスキーマ・テーブル名 |
policy_name | ポリシー名(テーブル内で一意) |
function_schema / policy_function | ポリシー関数のスキーマと関数名 |
sec_relevant_cols | 列レベル制御の対象列(カンマ区切り) |
sec_relevant_cols_opt | DBMS_RLS.ALL_ROWS で NULL 表示モードを指定 |
statement_types | 適用する DML 種別(デフォルト: SELECT,INSERT,UPDATE,DELETE) |
| ビュー | 内容 |
|---|---|
ALL_POLICIES | 作成されたポリシー一覧(関数名・適用 DML・ポリシータイプなど) |
ALL_POLICY_GROUPS | ポリシーグループ情報 |
ALL_POLICY_CONTEXTS | ポリシーコンテキスト(SYS_CONTEXT の名前空間など) |
このサイトで扱う内容
Section titled “このサイトで扱う内容”HR スキーマの EMPLOYEES 表を対象に、SALES_APP ユーザーへの行・列レベル制御を設定します。
HR ユーザーは全 107 行・全列を参照でき、SALES_APP ユーザーは営業系の 35 行のみ・SALARY 列は非表示(または NULL)になることを確認します。
-
SYS_CONTEXT('USERENV', 'SESSION_USER')を利用したポリシー関数を作成し、DBMS_RLS.ADD_POLICYでポリシーを適用します。HRは全件、SALES_APPはJOB_ID LIKE 'SA_%'の 35 行のみ参照できることを確認します。 -
sec_relevant_cols => 'SALARY'で列を指定したポリシーを追加します。SALES_APPで SALARY 列を含むクエリを実行すると行が除外されること、sec_relevant_cols_opt => DBMS_RLS.ALL_ROWSを指定すると NULL 表示になることを確認します。 -
DBMS_RLS.DROP_POLICYでポリシーを削除し、ポリシー関数もDROP FUNCTIONで削除します。ALL_POLICIESで削除を確認します。
関連ブログと手順の差分
Section titled “関連ブログと手順の差分”このサイトのチュートリアルをベースに、Oracle Blogs で解説記事を公開しています。記事では手順を再構成し、説明の追加・変更を行っているため、主な差分を以下にまとめます。
実行環境・実行ユーザー
Section titled “実行環境・実行ユーザー”-
ブログは Oracle AI Database 26ai FREE / PDB
FREEPDB1を前提とし、ポリシー関数とポリシーの作成・削除をすべてSYS(AS SYSDBA) で実行します。このサイトの手順は実行ユーザーを明示していません(関数はHRスキーマに作成)。 -
ブログでは
SALES_APPの作成と権限付与を明示しています。表単位の付与で、SELECT ANY TABLEは使いません。create user sales_app identified by "<password>";grant create session to sales_app;grant select on hr.employees to sales_app;
ポリシー追加時の statement_types
Section titled “ポリシー追加時の statement_types”- ブログは
DBMS_RLS.ADD_POLICYにstatement_types => 'SELECT'を明示し、ポリシーを SELECT のみに限定します(DML への予期しない影響を避けるため)。このサイトの手順はstatement_typesを省略しており、既定のSELECT,INSERT,UPDATE,DELETEが対象になります(ALL_POLICIESのUPD/DELがYESになる)。 DBMS_RLS.ADD_POLICY/DROP_POLICYは処理の前後で暗黙的にコミットします。未コミットの変更があるセッションでは実行しないでください。
列レベル VPD の確認
Section titled “列レベル VPD の確認”- このサイトは「既定(条件を満たさない行を除外)」→「
DBMS_RLS.ALL_ROWS(対象列を NULL 表示)」の順に両方の挙動を確認します。ブログは最初からDBMS_RLS.ALL_ROWSのみを使用します。 - ブログは列オプションの確認に
DBA_SEC_RELEVANT_COLS(SEC_REL_COLUMN/COLUMN_OPTIONがALL_ROWS)を使用します。このサイトはALL_POLICIESのみ。 - ブログでは行制御に業務条件を重ねる確認(
WHERE salary >= 10000と VPD 述語の AND 結合)も追加しています。
統合監査で VPD 述語を確認(#15 で追加、このサイトには無い内容)
Section titled “統合監査で VPD 述語を確認(#15 で追加、このサイトには無い内容)”-
HR.EMPLOYEESへの SELECT を記録する統合監査ポリシーを作成し、SALES_APPに対して有効化します。CREATE AUDIT POLICY vpd_employees_audit_policyACTIONS SELECT ON HR.EMPLOYEES;AUDIT POLICY vpd_employees_audit_policy BY SALES_APP; -
UNIFIED_AUDIT_TRAILのRLS_INFO列に、適用された VPD ポリシーごとにPOLICY_TYPE/POLICY_SCHEMA/POLICY_NAME/PREDICATEが連結記録されます(複数ポリシーが効く場合は複数分)。CLOB のためSET LONG 100000/SET LONGCHUNKSIZE 100000を先に実行します。 -
DBMS_AUDIT_UTIL.DECODE_RLS_INFO_ATRAIL_UNIを使うと、RLS_INFOをポリシーごとの行(RLS_PREDICATE/RLS_POLICY_NAMEなど)に展開できます。 -
ポイント:
SQL_TEXT自体は書き換わらず、VPD が適用した述語は別項目として記録されます。 -
後片付けは
NOAUDIT POLICY ... BY SALES_APP;→DROP AUDIT POLICY ...の順。
後片付けの順序
Section titled “後片付けの順序”- ブログは ポリシー削除 → 関数削除 の順です(このサイトの手順 3 は関数削除 → ポリシー削除の順)。どちらでも削除自体は可能ですが、関数を先に削除するとポリシーが有効なまま残り、対象表への SELECT がポリシー関数エラーになります。
- ハンズオン専用に
SALES_APPを作成した場合はDROP USER SALES_APP CASCADE;も実施します。
クライアント識別子とアプリケーション・コンテキスト(#14 の補足)
Section titled “クライアント識別子とアプリケーション・コンテキスト(#14 の補足)”接続プールで同一 DB ユーザーを共有する構成では SESSION_USER で利用者を区別できないため、次のいずれかの値を SYS_CONTEXT で参照してポリシー関数の述語を切り替えます。
| 項目 | クライアント識別子 | アプリケーション・コンテキスト |
|---|---|---|
| 保持する情報 | 組み込み USERENV 名前空間の単一の識別値 | 独自の名前空間に複数の属性・値 |
| 設定方法 | DBMS_SESSION.SET_IDENTIFIER / OCI・クライアントドライバ | CREATE CONTEXT で名前空間と PL/SQL パッケージを関連付け、そのパッケージから DBMS_SESSION.SET_CONTEXT |
| 参照方法 | SYS_CONTEXT('USERENV','CLIENT_IDENTIFIER') | SYS_CONTEXT('<名前空間>','<属性名>') |
接続プールのセッションを別の利用者へ再利用する際は、前の利用者の値をクリアまたは再設定する必要があります。このサイトの CLIENT_IDENTIFIER を使ったアクセス制御 は、アプリケーション・コンテキストを作らず共通 APP ユーザーに VIEWER / EDITOR / ADMIN のクライアント識別子を設定する例です。