ここしばらくは26aiでリリースされた新しいアクセス制御 Deep Data Securityの機能についての解説や使い方を紹介してきました。一方で、従来からOracle Databaseに行・列レベルのアクセス制御の機能としてVPD (Virtual Private Database)があるのですが、それとの違い・使い分けについて少し考えてみたいと思います。
まずVPDの機能についてですが、こちらのスライドをご覧ください。
VPDは、Oracle Database 8iの頃からある伝統的な機能で、データベース・ユーザーのセッション情報を基にしてWHERE句を透過的に付加するというものです。セッション情報にはユーザ名やIPアドレス、利用プログラムなどの情報が格納されていますが、それをSYS_CONTEXT関数で取り出し、VPDのポリシーとして利用します。
実際には下記のようにPL/SQLでVPDポリシーを表に対して作成する必要があります。20年以上前に設計された機能なので、設定方法は正直洗練されていませんが、グローバルでは軍事や公共機関などの最重要データベースのアクセス制御で使われている実績多数の信頼性の高い機能です。
VPDは、データベースユーザーを対象とします。Deep Data Securityは、エンドユーザーが対象なのでここが大きな違いです。とはいえ、VPDでアプリケーションユーザーの情報を使ったアクセス制御をしたいというのは昔からある要件なので、その場合は、Client Identifierまたはアプリケーション・コンテキストを使います。
下記のように、アプリケーションコード内で明示的にDBセッションの中にアプリケーション情報を格納するというものです。WebLogicや一部のアプリケーションでは既にWebUIで連携実装できるものもありますが、基本的にはコードを改修して、アプリの情報をDBセッションに伝播し、それを基にVPDのポリシーに利用します。
Deep Data Securityでは、エンドユーザー・セキュリティ・コンテキストなどでこの情報の伝播を自動的に行い、データ権限のポリシー設定で簡単に実装できるようになっています。
では、実際にこのVPDの機能を試す手順を紹介します。VPDは、Enterprise Editionの機能なので、お手元にある19cや26aiのOracle Databaseで簡単に試していただくことが可能です。
VPDの実行手順
①DBユーザーごとに自分の行だけを表示
表とユーザーの作成
CREATE TABLE orders_by_db_user (
order_id NUMBER PRIMARY KEY,
db_username VARCHAR2(128),
customer VARCHAR2(30),
order_amount NUMBER
);
INSERT INTO orders_by_db_user VALUES (1001, 'TOKYO_USER', 'TOKYO_STORE', 120000);
INSERT INTO orders_by_db_user VALUES (1002, 'OSAKA_USER', 'OSAKA_STORE', 85000);
INSERT INTO orders_by_db_user VALUES (1003, 'NAGOYA_USER', 'NAGOYA_STORE', 64000);
COMMIT;
CREATE USER tokyo_user IDENTIFIED BY password;
CREATE USER osaka_user IDENTIFIED BY password;
GRANT CREATE SESSION TO tokyo_user;
GRANT CREATE SESSION TO osaka_user;
GRANT SELECT ON orders_by_db_user TO tokyo_user;
GRANT SELECT ON orders_by_db_user TO osaka_user;
VPDポリシーの作成
CREATE OR REPLACE FUNCTION orders_user_pred (
p_schema IN VARCHAR2,
p_object IN VARCHAR2
)
RETURN VARCHAR2
AUTHID DEFINER
AS
BEGIN
RETURN q'[
db_username = SYS_CONTEXT('USERENV', 'SESSION_USER')
]';
END;
/
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'ADMIN',
object_name => 'ORDERS_BY_DB_USER',
policy_name => 'ORDERS_USER_POLICY',
function_schema => 'ADMIN',
policy_function => 'ORDERS_USER_PRED',
statement_types => 'SELECT',
policy_type => DBMS_RLS.DYNAMIC
);
END;
/
TokyoとOsakaのユーザーで動作確認
--Tokyoユーザーで実行
sql tokyo_user/password@サービス名
SQL> SELECT SYS_CONTEXT('USERENV', 'SESSION_USER') AS db_user FROM dual;
DB_USER
_____________
TOKYO_USER
SQL> SELECT order_id, customer, order_amount FROM admin.orders_by_db_user ORDER BY order_id;
ORDER_ID CUSTOMER ORDER_AMOUNT
___________ ______________ _______________
1001 TOKYO_STORE 120000
--Osakaユーザーで実行
sql osaka_user/password@サービス名
SQL> SELECT SYS_CONTEXT('USERENV', 'SESSION_USER') AS db_user FROM dual;
DB_USER
_____________
OSAKA_USER
SQL> SELECT order_id, customer, order_amount FROM admin.orders_by_db_user ORDER BY order_id;
ORDER_ID CUSTOMER ORDER_AMOUNT
___________ ______________ _______________
1002 OSAKA_STORE 85000
②接続元IPとクライアント・プログラムでアクセス制御
表とユーザーの作成
CREATE TABLE orders_by_network (
order_id NUMBER PRIMARY KEY,
customer VARCHAR2(30),
order_amount NUMBER
);
INSERT INTO orders_by_network VALUES (2001, 'TOKYO_STORE', 120000);
INSERT INTO orders_by_network VALUES (2002, 'OSAKA_STORE', 85000);
INSERT INTO orders_by_network VALUES (2003, 'NAGOYA_STORE', 64000);
COMMIT;
CREATE USER vpd_test IDENTIFIED BY password;
GRANT CREATE SESSION TO vpd_test;
GRANT SELECT ON orders_by_network TO vpd_test;
--実行クライアントのIPアドレスとプログラム名を控える
SELECT
SYS_CONTEXT('USERENV', 'IP_ADDRESS') AS ip_address,
SYS_CONTEXT('USERENV', 'CLIENT_PROGRAM_NAME') AS client_program
FROM dual;
IP_ADDRESS CLIENT_PROGRAM
_______________ _________________
116.82.xx.xx SQLcl
VPDポリシーの作成
CREATE OR REPLACE FUNCTION orders_net_pred (
p_schema IN VARCHAR2,
p_object IN VARCHAR2
)
RETURN VARCHAR2
AUTHID DEFINER
AS
BEGIN
RETURN q'[
SYS_CONTEXT('USERENV', 'IP_ADDRESS') = '116.82.xx.xx'
AND SYS_CONTEXT('USERENV', 'CLIENT_PROGRAM_NAME') = 'SQLcl'
]';
END;
/
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'ADMIN',
object_name => 'ORDERS_BY_NETWORK',
policy_name => 'ORDERS_NET_POLICY',
function_schema => 'ADMIN',
policy_function => 'ORDERS_NET_PRED',
statement_types => 'SELECT',
policy_type => DBMS_RLS.DYNAMIC
);
END;
/
vpd_testユーザーで動作確認
sql vpd_test/password@サービス名
SQL> SELECT
2 SYS_CONTEXT('USERENV', 'IP_ADDRESS') AS ip_address,
3 SYS_CONTEXT('USERENV', 'CLIENT_PROGRAM_NAME') AS client_program
4* FROM dual;
IP_ADDRESS CLIENT_PROGRAM
_______________ _________________
116.82.xx.xx SQLcl
-- ポリシーに指定したIPアドレスとプログラム名の組み合わせであればアクセスできる
SQL> SELECT * FROM .orders_by_network;
ORDER_ID CUSTOMER ORDER_AMOUNT
___________ _______________ _______________
2001 TOKYO_STORE 120000
2002 OSAKA_STORE 85000
2003 NAGOYA_STORE 64000
-- VPDポリシーのプログラム名をデタラメに変更し、再度実行する
SQL> SELECT * FROM admin.orders_by_network;
行が選択されていません
③CLIENT_IDENTIFIERによるアプリケーション・ユーザー単位でアクセス制御
表とユーザーの作成
CREATE TABLE orders_by_client_id (
order_id NUMBER PRIMARY KEY,
customer_email VARCHAR2(128),
order_amount NUMBER
);
INSERT INTO orders_by_client_id VALUES (3001, 'emma@example.com', 120000);
INSERT INTO orders_by_client_id VALUES (3002, 'liam@example.com', 85000);
INSERT INTO orders_by_client_id VALUES (3003, 'emma@example.com', 64000);
COMMIT;
CREATE USER app_user IDENTIFIED BY password;
GRANT CREATE SESSION TO app_user;
GRANT SELECT ON orders_by_client_id TO app_user;
VPDポリシーの作成
CREATE OR REPLACE FUNCTION orders_app_pred (
p_schema IN VARCHAR2,
p_object IN VARCHAR2
)
RETURN VARCHAR2
AUTHID DEFINER
AS
BEGIN
RETURN q'[
customer_email =
SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER')
]';
END;
/
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'ADMIN',
object_name => 'ORDERS_BY_CLIENT_ID',
policy_name => 'ORDERS_APP_POLICY',
function_schema => 'ADMIN',
policy_function => 'ORDERS_APP_PRED',
statement_types => 'SELECT',
policy_type => DBMS_RLS.DYNAMIC
);
END;
/
CLIENT IDENTIFIERにアプリケーションIDを格納して動作確認
sql app_user/password@サービス名
--emmaのidをClient Identifierに設定
SQL> BEGIN
2 DBMS_SESSION.SET_IDENTIFIER(
3 'emma@example.com'
4 );
5 END;
6* /
PL/SQLプロシージャが正常に完了しました。
SQL> SELECT SYS_CONTEXT('USERENV','CLIENT_IDENTIFIER') AS application_user FROM dual;
APPLICATION_USER
___________________
emma@example.com
--emmaのレコードだけアクセス可
SQL> SELECT order_id, customer_email, order_amount FROM admin.orders_by_client_id ORDER BY order_id;
ORDER_ID CUSTOMER_EMAIL ORDER_AMOUNT
___________ ___________________ _______________
3001 emma@example.com 120000
3003 emma@example.com 64000
-- liamのidをClient Identiferに設定
SQL> BEGIN
2 DBMS_SESSION.SET_IDENTIFIER(
3 'liam@example.com'
4 );
5 END;
6* /
PL/SQLプロシージャが正常に完了しました。
SQL> SELECT SYS_CONTEXT('USERENV','CLIENT_IDENTIFIER') AS application_user FROM dual;
APPLICATION_USER
___________________
liam@example.com
--liamのレコードだけアクセス可
SQL> SELECT order_id, customer_email, order_amount FROM admin.orders_by_client_id ORDER BY order_id;
ORDER_ID CUSTOMER_EMAIL ORDER_AMOUNT
___________ ___________________ _______________
3002 liam@example.com 85000
簡単なVPDポリシーを作成し、その動作を確認しました。1つの表には複数のVPDポリシーを設定できます。複数のポリシー条件は、基本的にAND条件として組み合わされます。このため、VPDは、アクセス範囲をポリシー条件によって絞り込む「引き算型」のアクセス制御と捉えられます。
一方、Deep Data Securityは、アクセス権がない状態を起点として、エンドユーザーに付与されたデータ権限を積み重ねます。最終的なアクセス範囲は、適用されるデータ権限の和集合となるため、「足し算型」のアクセス制御と捉えられます。
改めてVPDとDeep Data Securityの違いは以下のような感じです。同じ行・列アクセス制御の機能ですが、それぞれの特徴や要件に応じて使用を検討されると良いかと思います。
とはいえ、VPDとDeep Data Securityのどちらを選択すべきか迷う場合もあるため、簡単な選択ガイドを作成しました。判断のポイントは、アクセス制御を対象とする主な利用者、データベースのバージョン、接続に使用するクライアントや開発プラットフォームです。
特に、Deep Data Securityのエンドユーザー・セキュリティ・コンテキストやトークン認証は、現時点では対応していないローコード/ノーコード・ツールやAIエージェント開発プラットフォームもあります。その場合は、対応するツールを選択するか、対応されるまでプログラムから接続処理を実装する必要があります。
Select AIやAIエージェント関連ツールからデータベースへアクセスする場合でも、VPDとDeep Data Securityのアクセス制御はデータベース側で強制されます。AIを利用したデータアクセスにおいても、データベース側の基本的なセキュリティ機能として活用できます。
MFAやトークン認証については下記の記事を参考にして下さい。
Oracle Databaseのトークン・ベースの外部認証連携
Oracle Databaseのローカル・ユーザーに対するMFA有効化




