前回は、HR.EMPLOYEES 表へVPDの行制御ポリシーを追加し、VPD によって SALES_APP ユーザーには営業職の行だけが返ることを確認しました。
VPD は Where句を暗黙的に追加する、ということから行レベルの制御が中心になりますが、列レベルでも制御が可能です。
そこで、本記事では追加の手順として、SALARY 列を対象とする列レベルVPDを追加します。
最後には、統合監査から、VPDが実際に適用した述語を確認することまでを行ってみます。
0. はじめに
本記事で実施する内容は次のとおりです。
| 実施内容 | 列レベルVPDと統合監査の設定、動作確認 ・ SALARY 列を対象にしたVPDポリシー関数の作成・ DBMS_RLS.ALL_ROWS による給与列の NULL 表示の確認・統合監査ポリシーの作成と RLS_INFO の確認・作成した統合監査ポリシーとVPDポリシーの削除 |
| 作業時間の目安 | 第14回の行制御が設定済みの状態で 約 30 分 |
| 実行環境 | ・Oracle AI Database 26ai FREE ・PDB 名: FREEPDB1・HR サンプルスキーマ ・クライアントツール: SQLcl または SQL*Plus |
本記事では、見やすさのために実行結果の一部を省略または整形しています。
そのため、実際の出力と異なる場合があります。
この手順は、HR.EMPLOYEES 表へ第14回の行制御ポリシーが設定されている専用PDBで実行してください。
第14回の「8. 後片付け」を実行済みの場合は、先に第14回の手順でVPDポリシー( EMPLOYEES_VPD_POLICY)と ポリシー関数(GET_SALES_PREDICATE)を作り直します。
※ 第14回の後片付けで SALES_APP ユーザーも削除した場合は、第14回の「1-3. SALES_APP ユーザーの準備」も実行するようにしてください
1. 前提条件と事前確認
1-1. 第14回で作成したオブジェクトの確認
SYS ユーザーで FREEPDB1 に接続します。
$ sql sys/<Password>@localhost:1521/freepdb1 AS SYSDBA
SHOW USER
SHOW CON_NAME
SELECT object_owner,
object_name,
policy_name,
function,
sel,
enable
FROM DBA_POLICIES
WHERE object_owner = 'HR'
AND object_name = 'EMPLOYEES'
AND policy_name = 'EMPLOYEES_VPD_POLICY';
SELECT object_name, object_type, status
FROM DBA_OBJECTS
WHERE owner = 'HR'
AND object_name = 'GET_SALES_PREDICATE';
次のように、行制御ポリシーと関数が有効であることを確認します。
OBJECT_OWNER OBJECT_NAME POLICY_NAME FUNCTION SEL ENABLE
------------- ------------ --------------------- ---------------------- ---- -------
HR EMPLOYEES EMPLOYEES_VPD_POLICY GET_SALES_PREDICATE YES YES
OBJECT_NAME OBJECT_TYPE STATUS
----------------------- -------------- -------
GET_SALES_PREDICATE FUNCTION VALID
EMPLOYEES_VPD_POLICY がない場合、第14回の「4. VPD による行制御を設定する」までを実行してから進めてください。
以下、一括で実行するためのコマンドです
行制御の設定を一括で実行する
CREATE OR REPLACE FUNCTION HR.GET_SALES_PREDICATE (
P_SCHEMA IN VARCHAR2,
P_TABLE IN VARCHAR2
) RETURN VARCHAR2
IS
V_PREDICATE VARCHAR2(400);
BEGIN
IF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'SALES_APP' THEN
V_PREDICATE := 'JOB_ID LIKE ''SA_%''';
ELSE
V_PREDICATE := '1=1';
END IF;
RETURN V_PREDICATE;
END GET_SALES_PREDICATE;
/
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMPLOYEES_VPD_POLICY',
function_schema => 'HR',
policy_function => 'GET_SALES_PREDICATE',
statement_types => 'SELECT'
);
END;
/
このあとの手順では SALES_APP ユーザーで接続します。
ユーザーを削除している場合は、第14回の「1-3. SALES_APP ユーザーの準備」を実行し、接続権限と HR.EMPLOYEES 表の参照権限を付与してください。
1-2. 統合監査を利用できることを確認する
ここでは、CREATE AUDIT POLICY 文で統合監査ポリシーを作成し、UNIFIED_AUDIT_TRAIL ビューで監査証跡を確認します。
まず、引き続きSYSユーザーにて統合監査が使用できることを、監査ポリシーのディクショナリビューで確認します。
SELECT count(*) AS POLICY_COUNT FROM audit_unified_policies;
この問い合わせが失敗する場合、対象データベースで統合監査が有効か、監査を管理または参照する権限があるかを確認してください。(統合監査はデフォルトで有効となっています)
※ 監査ポリシーの作成には AUDIT SYSTEM システム権限または AUDIT_ADMIN ロールが必要です。
※ 統合監査証跡を参照するには、通常、AUDIT_VIEWER ロールまたは監査管理者の権限が必要です。
1-3. 既存の列ポリシーと監査ポリシーを確認する
同名のVPDポリシー、ポリシー関数、統合監査ポリシーがないことを確認します。
SELECT object_owner,
object_name,
policy_name,
function,
sel,
enable
FROM DBA_POLICIES
WHERE object_owner = 'HR'
AND object_name = 'EMPLOYEES'
AND policy_name = 'EMPLOYEES_SALARY_COL_VPD_POLICY';
SELECT object_name, object_type, status
FROM DBA_OBJECTS
WHERE owner = 'HR'
AND object_name = 'GET_MASKING_SALARY_COL';
SELECT policy_name, audit_option, object_schema, object_name
FROM AUDIT_UNIFIED_POLICIES
WHERE policy_name = 'VPD_EMPLOYEES_AUDIT_POLICY';
初めて実行する環境では、いずれも次のように表示されることを確認します。
no rows selected
2. デモ構成の概要
前回行った行制御では、SALES_APP に JOB_ID LIKE 'SA_%' が適用され、この結果、SALES_APP が HR.EMPLOYEES 表を参照すると、営業職の 35 行だけが返りました。
本記事では、その表に SALARY をセキュリティ関連列とする二つ目のVPDポリシーを追加します。
列レベルVPDでは、sec_relevant_cols にセキュリティ関連列を指定します。
今回のように SALARY を指定すると、SALARY 列を参照する問い合わせに対して列レベルVPDが適用されます。
そのうえで、sec_relevant_cols_opt の設定によって、次の二つの動作を選択できます。
NULL(デフォルト):ポリシー関数が返す述語を行の絞り込み条件として適用し、述語を満たさない行を結果から除外します。DBMS_RLS.ALL_ROWS:列レベルVPDでは行を除外せず、述語を満たさない行のセキュリティ関連列をNULLとして返します。SQLの検索条件や、ほかのVPDポリシーによる行制御は引き続き適用されます。

本記事のハンズオンでは、行を残したまま対象列を隠す DBMS_RLS.ALL_ROWS だけを使用します。
ポリシー関数は、SALES_APP に対して常に偽となる 1=2 を返します。
このため、前回の行制御で残った営業職の 35 行が返りますが、すべての行で SALARY 列が NULL になります。
※ NULL を想定していない集計、並べ替え、アプリケーションの処理へ影響するため、本番利用時はアプリケーションが実行するSQLで事前に動作を確認するようにしてください。
3. SALARY 列を制御するVPDポリシーを追加する
3-1. ポリシー関数を作成する
引き続きSYSユーザーで接続したまま、ポリシー関数を作成します。
次の関数は、SALES_APP の場合だけ 1=2 を返し、それ以外のユーザーには NULL を返します。
CREATE OR REPLACE FUNCTION HR.GET_MASKING_SALARY_COL (
P_SCHEMA IN VARCHAR2,
P_TABLE IN VARCHAR2
) RETURN VARCHAR2
IS
V_PREDICATE VARCHAR2(400);
BEGIN
IF SYS_CONTEXT('USERENV', 'SESSION_USER') = 'SALES_APP' THEN
V_PREDICATE := '1=2';
END IF;
RETURN V_PREDICATE;
END GET_MASKING_SALARY_COL;
/
Function HR.GET_MASKING_SALARY_COL compiled と表示され、正しくコンパイルされたことを確認します。
3-2. SALARY 列に列レベルVPDポリシーを追加する
次に SALARY 列をセキュリティ関連列とするポリシーを追加します。
ここでは対象SQLを SELECT に限定します。
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',
statement_types => 'SELECT',
sec_relevant_cols => 'SALARY',
sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS
);
END;
/
設定を確認します。
SELECT object_owner,
object_name,
policy_name,
function,
sel,
enable
FROM DBA_POLICIES
WHERE object_owner = 'HR'
AND object_name = 'EMPLOYEES';
SELECT object_owner,
object_name,
policy_name,
sec_rel_column,
column_option
FROM DBA_SEC_RELEVANT_COLS
WHERE object_owner = 'HR'
AND object_name = 'EMPLOYEES';
OBJECT_OWNER OBJECT_NAME POLICY_NAME FUNCTION SEL ENABLE
_______________ ______________ __________________________________ _________________________ ______ _________
HR EMPLOYEES EMPLOYEES_SALARY_COL_VPD_POLICY GET_MASKING_SALARY_COL YES YES
HR EMPLOYEES EMPLOYEES_VPD_POLICY GET_SALES_PREDICATE YES YES
OBJECT_OWNER OBJECT_NAME POLICY_NAME SEC_REL_COLUMN COLUMN_OPTION
_______________ ______________ __________________________________ _________________ ________________
HR EMPLOYEES EMPLOYEES_SALARY_COL_VPD_POLICY SALARY ALL_ROWS
COLUMN_OPTION が ALL_ROWS であることを確認します。
4. SALARY 列がNULLになることを確認する
SALES_APP で接続したセッションで、SALARY を含む問い合わせを実行します。
$ sql sales_app@localhost:1521/freepdb1
SELECT employee_id, first_name, salary, job_id FROM HR.EMPLOYEES ORDER BY employee_id;
実行例です。
SQL> SELECT employee_id, first_name, salary, job_id FROM HR.EMPLOYEES ORDER BY employee_id;
EMPLOYEE_ID FIRST_NAME SALARY JOB_ID
______________ ______________ _________ _________
145 John SA_MAN
146 Karen SA_MAN
147 Alberto SA_MAN
148 Gerald SA_MAN
149 Eleni SA_MAN
150 Sean SA_REP
151 David SA_REP
152 Peter SA_REP
153 Christopher SA_REP
...(省略)...
172 Elizabeth SA_REP
173 Sundita SA_REP
174 Ellen SA_REP
175 Alyssa SA_REP
176 Jonathon SA_REP
177 Jack SA_REP
178 Kimberely SA_REP
179 Charles SA_REP
35 rows selected.
行数は 35 行のままですが、SALARY 列はすべて空欄(NULL)になっています。
DBMS_RLS.ALL_ROWS を指定すると、列ポリシーの述語を満たさない行も除外されず、セキュリティ関連列が NULL になります。
この例では 1=2 がすべての行で偽になるため、営業職の 35 行を残したまま、すべての SALARY が NULL になります。
また、SELECT * FROM HR.EMPLOYEES のように列名を明示的に指定しないときも、 SALARY 列を含む場合は同じ結果となります。
5. 統合監査で適用されたVPD述語を確認する
VPDでは設定に応じて暗黙的なSQLが実行されるとあれば、監査ではどのように記録されるのでしょうか。ここからの手順では、Oracle Database の監査機能「統合監査」ではVPDはどのように記録されるかを確認します。
5-1. SELECT を記録する統合監査ポリシーを作成する
監査情報を確認するには、HR.EMPLOYEES 表に対する SELECT を記録する監査ポリシーを有効にしてから、対象の問い合わせを実行します。
SYS ユーザーで次の監査ポリシーを作成し、SALES_APP ユーザーに対して有効にします。
CREATE AUDIT POLICY vpd_employees_audit_policy
ACTIONS SELECT ON HR.EMPLOYEES;
AUDIT POLICY vpd_employees_audit_policy BY SALES_APP;
作成と有効化の状態を確認します。
SELECT policy_name, audit_option, object_schema, object_name
FROM audit_unified_policies
WHERE policy_name = 'VPD_EMPLOYEES_AUDIT_POLICY';
SELECT policy_name, entity_name, enabled_option, success, failure
FROM audit_unified_enabled_policies
WHERE policy_name = 'VPD_EMPLOYEES_AUDIT_POLICY';
POLICY_NAME AUDIT_OPTION OBJECT_SCHEMA OBJECT_NAME
_____________________________ _______________ ________________ ______________
VPD_EMPLOYEES_AUDIT_POLICY SELECT HR EMPLOYEES
POLICY_NAME ENTITY_NAME ENABLED_OPTION SUCCESS FAILURE
_____________________________ ______________ _________________ __________ __________
VPD_EMPLOYEES_AUDIT_POLICY SALES_APP BY USER YES YES
※ AUDIT_UNIFIED_ENABLED_POLICIES の列名や表示値はリリースにより異なる場合があります。
5-2. SALES_APP ユーザーで監査対象の SELECT を実行する
新しい SALES_APP セッションを開き、監査対象の問い合わせを実行します。
$ sql sales_app@localhost:1521/freepdb1
SELECT * FROM HR.EMPLOYEES;
SELECT employee_id, first_name, salary, job_id
FROM HR.EMPLOYEES
WHERE employee_id BETWEEN 145 AND 150
ORDER BY employee_id;
先ほどと問題なく VPD が機能していることを確認してください。
5-3. RLS_INFO を確認する
SYS ユーザー(または AUDIT_VIEWER ロールを持つ監査閲覧ユーザー)で FREEPDB1 に接続し、直前2つの監査レコードを確認します。
-- SQLcl/SQL*Plusでは、CLOB の表示上限が制限されているため(デフォルト 80bytes)、出力が途中で切れないよう、以下を事前に実行する
SET LONG 100000
SET LONGCHUNKSIZE 100000
SELECT event_timestamp,
dbusername,
action_name,
object_schema,
object_name,
sql_text,
rls_info
FROM UNIFIED_AUDIT_TRAIL
WHERE dbusername = 'SALES_APP'
AND action_name = 'SELECT'
AND object_schema = 'HR'
AND object_name = 'EMPLOYEES'
ORDER BY event_timestamp DESC
FETCH FIRST 2 ROW ONLY;
RLS_INFO には、適用されたVPDポリシーごとにポリシー種別、所有者、名前、述語が連結して記録されます。
EVENT_TIMESTAMP DBUSERNAME ACTION_NAME OBJECT_SCHEMA OBJECT_NAME SQL_TEXT RLS_INFO
__________________________________ _____________ ______________ ________________ ______________ _________________________________________________ ____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
22-AUG-26 02.08.06.814186000 AM SALES_APP SELECT HR EMPLOYEES SELECT employee_id, first_name, salary, job_id ((POLICY_TYPE=[3]'VPD'),(POLICY_SCHEMA=[2]'HR'),(POLICY_NAME=[20]'EMPLOYEES_VPD_POLICY'),(PREDICATE=[18]'JOB_ID LIKE 'SA_%''));((POLICY_TYPE=[3]'VPD'),(POLICY_SCHEMA=[2]'HR'),(POLICY_NAME=[31]'EMPLOYEES_SALARY_COL_VPD_POLICY'),(PREDICATE=[3]'1=2'));
FROM HR.EMPLOYEES
WHERE employee_id BETWEEN 145 AND 150
ORDER BY employee_id
22-AUG-26 02.07.55.388065000 AM SALES_APP SELECT HR EMPLOYEES SELECT * FROM HR.EMPLOYEES ((POLICY_TYPE=[3]'VPD'),(POLICY_SCHEMA=[2]'HR'),(POLICY_NAME=[20]'EMPLOYEES_VPD_POLICY'),(PREDICATE=[18]'JOB_ID LIKE 'SA_%''));((POLICY_TYPE=[3]'VPD'),(POLICY_SCHEMA=[2]'HR'),(POLICY_NAME=[31]'EMPLOYEES_SALARY_COL_VPD_POLICY'),(PREDICATE=[3]'1=2'));
統合監査の RLS_INFO にはVPDポリシー名と述語が記録されます。
一つのSQLに複数のVPDポリシーが適用される場合、RLS_INFO には複数のポリシー情報が記録されていることがわかります。
5-4. RLS_INFO を列に展開して確認する
DBMS_AUDIT_UTIL.DECODE_RLS_INFO_ATRAIL_UNI を使うと、RLS_INFO の内容をポリシーごとの行に展開できます。
SELECT dbusername,
action_name,
object_schema,
object_name,
sql_text,
rls_predicate,
rls_policy_type,
rls_policy_owner,
rls_policy_name
FROM TABLE(
DBMS_AUDIT_UTIL.DECODE_RLS_INFO_ATRAIL_UNI(
CURSOR(
SELECT *
FROM UNIFIED_AUDIT_TRAIL
WHERE dbusername = 'SALES_APP'
AND action_name = 'SELECT'
AND object_schema = 'HR'
AND object_name = 'EMPLOYEES'
ORDER BY event_timestamp DESC
FETCH FIRST 2 ROW ONLY
)
)
);
以下が実行例です。
DBUSERNAME ACTION_NAME OBJECT_SCHEMA OBJECT_NAME SQL_TEXT RLS_PREDICATE RLS_POLICY_TYPE RLS_POLICY_OWNER RLS_POLICY_NAME
_____________ ______________ ________________ ______________ _________________________________________________ _____________________ __________________ ___________________ __________________________________
SALES_APP SELECT HR EMPLOYEES SELECT employee_id, first_name, salary, job_id JOB_ID LIKE 'SA_%' VPD HR EMPLOYEES_VPD_POLICY
FROM HR.EMPLOYEES
WHERE employee_id BETWEEN 145 AND 150
ORDER BY employee_id
SALES_APP SELECT HR EMPLOYEES SELECT employee_id, first_name, salary, job_id 1=2 VPD HR EMPLOYEES_SALARY_COL_VPD_POLICY
FROM HR.EMPLOYEES
WHERE employee_id BETWEEN 145 AND 150
ORDER BY employee_id
SALES_APP SELECT HR EMPLOYEES SELECT * FROM HR.EMPLOYEES JOB_ID LIKE 'SA_%' VPD HR EMPLOYEES_VPD_POLICY
SALES_APP SELECT HR EMPLOYEES SELECT * FROM HR.EMPLOYEES 1=2 VPD HR EMPLOYEES_SALARY_COL_VPD_POLICY
監査証跡には、利用者が発行したSQLだけでなく、VPDがそのSQLに適用した述語も記録されます。しかし、SQL_TEXT 自体が変更されるわけではないことがわかります。
ハンズオン手順は以上です。
6. 後片付け
最後に監査ポリシー、列レベルVPDポリシー、列制御用関数の順に作成したリソースを削除します。
第14回で作成した行制御も不要であれば、最後に行ポリシーとポリシー関数を削除します。
6-1. 統合監査ポリシーを無効化して削除する
SYS ユーザーで、監査ポリシーを無効にしてから削除します。
NOAUDIT POLICY VPD_EMPLOYEES_AUDIT_POLICY BY SALES_APP;
DROP AUDIT POLICY VPD_EMPLOYEES_AUDIT_POLICY;
削除を確認します。
SELECT policy_name FROM AUDIT_UNIFIED_POLICIES WHERE policy_name = 'VPD_EMPLOYEES_AUDIT_POLICY';
6-2. 列レベルVPDポリシーと関数を削除する
SYS ユーザーで列レベルポリシーを削除します。
BEGIN
DBMS_RLS.DROP_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMPLOYEES_SALARY_COL_VPD_POLICY'
);
END;
/
次に、関数を削除します。
DROP FUNCTION HR.GET_MASKING_SALARY_COL;
6-3. 行制御も終了する場合の削除
このVPDハンズオンを終了する場合は、第14回で作成した行制御も削除します。
引き続き、SYS ユーザーでポリシーを削除します。
BEGIN
DBMS_RLS.DROP_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'EMPLOYEES_VPD_POLICY'
);
END;
/
行制御の関数を削除します。
DROP FUNCTION HR.GET_SALES_PREDICATE;
最後に、第14回と本記事で作成した二つのVPDポリシーが残っていないことを確認します。
SELECT policy_name
FROM DBA_POLICIES
WHERE object_owner = 'HR'
AND object_name = 'EMPLOYEES'
AND policy_name IN (
'EMPLOYEES_VPD_POLICY',
'EMPLOYEES_SALARY_COL_VPD_POLICY'
);
また、SALES_APP をこの二つのハンズオンのためだけに作成した場合は、ほかの検証で使用していないことを確認してから削除してください。
DROP USER SALES_APP CASCADE;
7. まとめ
本記事では、第14回で行ったの行制御に、SALARY 列を対象とする列レベルVPDと統合監査を追加し、以下を確認しました。
sec_relevant_cols => 'SALARY'を指定すると、給与列を参照するSQLだけで列レベルポリシーが評価DBMS_RLS.ALL_ROWSを指定した場合は、営業職の 35 行を返したまま給与列がNULLに- 統合監査の
RLS_INFOから、行制御と列制御のVPD述語を確認
なお、VPDを本番へ適用するときは、ポリシー関数だけでなく、利用者属性の信頼性、SQLへの影響、監査証跡の保護と保持も設計するようにしてください。