2025年8月18日月曜日

Entra IDで認証したMCPサーバーに仮想プライベート・データベースのポリシーを適用する

先日の記事「SQLclのMCPサーバーのデータベース接続にTOKEN_AUTH=AZURE_INTERACTIVEの設定を使用する」にて、SQLclのMCPサーバーからデータベースに接続する際に、Entra IDでユーザー認証する環境を作成しました。

この環境を使って、MCPサーバーからのアクセスに仮想プライベート・データベースのポリシーを設定してみます。

Autonomous DatabaseとしてSALESADBが作成済みであること、また、Entra IDでユーザー認証を行なってSALESADBに接続できる状態から作業を続けます。

作業ディレクトリにはSQLclを展開したディレクトリsqlcl、Wallet_SALESADB.zipを展開したディレクトリtns_admin、環境変数TNS_ADMINを設定した上でSQLclのMCPサーバーを起動するスクリプトsql-mcp.shがあります。

データベースSALESADBには、TNS名salesadb_lowsalesadb_azcodeおよびsalesadb_azintで接続できます。

azure % ls

pom.xml sqlcl target Wallet_SALESADB.zip

sql-mcp.sh sqlcl-latest.zip tns_admin

azure % 



サンプル・データセットのhuman resourcesのインストール



仮想プライベート・データベースの設定作業は、Oracle APEXでの記事「データベース・セキュリティの活用(7) - 仮想プライベート・データベース」とほぼ同じです。

本記事では表EMPの代わりにサンプル・スキーマのhuman resourcesに含まれる表EMPLOYEESを使用します。サンプル・データセットの表EMPLOYEESを使用するにあたって、表EMPの列EMPNOを列EMPLOYEE_IDに、DEPTNOをDEPARTMENT_IDに、SALをSALARYに、COMMをCOMMISSION_PCTにそれぞれ置き換えます。

最初にサンプル・データセットをダウンロードして解凍します。2025年8月18日時点ではv23.3.zipが最新です。

curl -OL https://github.com/oracle-samples/db-sample-schemas/archive/refs/tags/v23.3.zip
unzip v23.3.zip

azure % curl -OL https://github.com/oracle-samples/db-sample-schemas/archive/refs/tags/v23.3.zip

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

100 11.6M    0 11.6M    0     0  3930k      0 --:--:--  0:00:03 --:--:-- 4927k

azure % unzip v23.3.zip 

Archive:  v23.3.zip

e3325a83e56c516815844025418a96ecaf219751

   creating: db-sample-schemas-23.3

 extracting: db-sample-schemas-23.3/.gitignore  

  inflating: db-sample-schemas-23.3/LICENSE.txt  

  inflating: db-sample-schemas-23.3/README.md  

  inflating: db-sample-schemas-23.3/README.txt  

  inflating: db-sample-schemas-23.3/SECURITY.md  

   creating: db-sample-schemas-23.3/customer_orders


[中略]


  inflating: db-sample-schemas-23.3/sales_history/sh_populate.sql  

  inflating: db-sample-schemas-23.3/sales_history/sh_uninstall.sql  

  inflating: db-sample-schemas-23.3/sales_history/supplementary_demographics.csv  

  inflating: db-sample-schemas-23.3/sales_history/times.csv  

azure % 


human resourcesのディレクトリへ移動します。

cd db-sample-schemas-23.3/human_resources

azure % cd db-sample-schemas-23.3/human_resources 

human_resources % 


管理者ユーザーADMINでデータベースSALESADBに接続し、インストール・スクリプトhr_install.sqlを実行します。環境変数TNS_ADMIN$PWD/tns_adminが設定されていて、接続文字列としてsalesadb_lowが使用可能とします。

sql admin@salesadb_low
@hr_inistall

スキーマHRのパスワードおよびデフォルト表領域の設定を求められます。それらを設定すると、スキーマHRに表EMPLOYEESを含むサンプル・データセットがインストールされます。

human_resources % sql admin@salesadb_low



SQLcl: 月 8月 18 14:05:08 2025のリリース25.2 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


パスワード (**********?) **************

Last Successful login time: 月 8月  18 2025 14:05:23 +09:00


接続先:

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08


SQL> @hr_install


Thank you for installing the Oracle Human Resources Sample Schema.

This installation script will automatically exit your database session

at the end of the installation or if any error is encountered.

The entire installation will be logged into the 'hr_install.log' log file.


Enter a password for the user HR: **************



Enter a tablespace for HR [DATA]: 

Do you want to overwrite the schema, if it already exists? [YES|no]: 

******  Creating REGIONS table ....


Table REGIONSは作成されました。



INDEX REG_ID_PKは作成されました。



Table REGIONSが変更されました。


******  Creating COUNTRIES table ....


Table COUNTRIESは作成されました。



Table COUNTRIESが変更されました。



[中略]


コミットが完了しました。



Installation verification    

____________________________ 

Verification:                


Table             provided    actual 

______________ ___________ _________ 

regions                  5         5 

countries               25        25 

departments             27        27 

locations               23        23 

employees              107       107 

jobs                    19        19 

job_history             10        10 


Thank you!                                                  

___________________________________________________________ 

The installation of the sample schema is now finished.      

Please check the installation verification output above.    

                                                            

You will now be disconnected from the database.             

                                                            

Thank you for using Oracle Database!                        

                                                            

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08から切断されました

human_resources % 


サンプル・データセットのインストールが完了したら、先ほどの作業ディレクトリに戻ります。


管理ユーザーVPDADMINの作成と各種権限の付与



仮想プライベート・データベースを構成するためのデータベース・ユーザーとしてVPDADMINを作成します。

データベースSALESADBに管理者ユーザーADMINで接続し、作業を進めます。

データベース・ユーザーVPDADMINを作成し、最低限必要な権限を与えます。

create user vpdadmin identified by <パスワード>;
alter user vpdadmin quota 25m on data;
grant create session to vpdadmin;
grant create table to vpdadmin;

SQL> create user vpdadmin identified by **********;


User VPDADMINは作成されました。


SQL> alter user vpdadmin quota 25m on data;


User VPDADMINが変更されました。


SQL> grant create session to vpdadmin;


Grantが正常に実行されました。


SQL> grant create table to vpdadmin;


Grantが正常に実行されました。


SQL> 


VPDADMINに、仮想プラベート・データベースを構成するために必要な権限を与えます。データベースが23aiなので、権限ADMINISTER ROW LEVEL SECURITY POLICYも必要です。

grant create any context to vpdadmin;
grant execute on dbms_rls to vpdadmin;
grant execute on dbms_session to vpdadmin;
grant create procedure to vpdadmin;
grant administer row level security policy to vpdadmin;


SQL> grant create any context to vpdadmin;


Grantが正常に実行されました。


SQL> grant execute on dbms_rls to vpdadmin;


Grantが正常に実行されました。


SQL> grant execute on dbms_session to vpdadmin;


Grantが正常に実行されました。


SQL> grant create procedure to vpdadmin;


Grantが正常に実行されました。


SQL> grant administer row level security policy to vpdadmin;


Grantが正常に実行されました。


SQL> 


MCPサーバーの接続ユーザーMCPUSERに権限を追加します。スキーマHRの表を検索する権限と、ログオン・トリガーを作成する権限を与えます。

grant select any table on schema hr to mcpuser;
grant create trigger to mcpuser;


SQL> grant select any table on schema hr to mcpuser;


Grantが正常に実行されました。


SQL> grant create trigger to mcpuser;


Grantが正常に実行されました。


SQL> exit

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08から切断されました

azure % 


以上で、管理ユーザーVPDADMINの作成と各種権限の付与が完了しました。


仮想プライベート・データベースの構成



作成した管理ユーザーVPDADMINにて、仮想プライベート・データベースを構成します。

sql vpdadmin@salesadb_low

azure % sql vpdadmin@salesadb_low



SQLcl: 月 8月 18 14:40:33 2025のリリース25.2 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


パスワード (**********?) **************

接続先:

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08


SQL> 


Entra IDで認証されたユーザーは、システム・コンテキストUSERENVの属性AUTHENTICATED_IDENTITYにユーザーIDが設定されます。このEntra IDのユーザーをスキーマHRの表EMPLOYEESに保存されている従業員に対応させるために、表AUTH_USERSを作成します。
create table auth_users(
    employee_id number not null,
    department_id number not null,
    authenticated_identity varchar2(128) not null
);

SQL> create table auth_users(

  2      employee_id number not null,

  3      department_id number not null,

  4      authenticated_identity varchar2(128) not null

  5* );


Table AUTH_USERSは作成されました。


SQL> 


Entra IDで認証されたユーザーがEMPLOYEE_ID=105, DEPARTMENT_ID=60のDavid Williamsとして認識されるように、表AUTH_USERSに以下の1行を挿入します。

insert into auth_users(employee_id, department_id, authenticated_identity) values(105,60,'Entra IDのユーザーのID - upnとして渡される値');
commit;

SQL> insert into auth_users(employee_id, department_id, authenticated_identity) values(105,60,'yuji______outlook.com_EXT_@________outlook.onmicrosoft.com');


1行挿入しました。


SQL> commit;


コミットが完了しました。


SQL> 


アプリケーション・コンテキストEMP_DEPT_CTXを作成します。このアプリケーション・コンテキストに上記の表AUTH_USERSから検索された、Entra IDのユーザーに対応した従業員のEMPLOYEE_IDDEPARTMENT_IDを保存します。

create or replace context emp_dept_ctx using emp_dept_ctx_pkg;

SQL> create or replace context emp_dept_ctx using emp_dept_ctx_pkg;


Context EMP_DEPT_CTXは作成されました。


SQL> 


アプリケーション・コンテキストを操作するパッケージEMP_DEPT_CTX_PKGを作成します。


SQL> create or replace package emp_dept_ctx_pkg is

  2  procedure init;

  3  end;

  4* /


Package EMP_DEPT_CTX_PKGがコンパイルされました


SQL> create or replace package body emp_dept_ctx_pkg is

  2  procedure init

  3  is

  4      l_employee_id            vpdadmin.auth_users.employee_id%type;

  5      l_department_id          vpdadmin.auth_users.department_id%type;

  6      l_authenticated_identity vpdadmin.auth_users.authenticated_identity%type;

  7  begin

  8      /* 

  9       * 認証されたEntra IDのユーザーに対応する従業員のEMPLOYEE_IDとDEPARTMENT_IDを取り出す。

 10       */

 11      select employee_id, department_id into l_employee_id, l_department_id

 12      from vpdadmin.auth_users

 13      where authenticated_identity = sys_context('USERENV', 'AUTHENTICATED_IDENTITY');

 14      /*

 15       * アプリケーション・コンテキストにEMPLOYEE_IDとDEPARTMENT_IDを設定する。

 16       */

 17      dbms_session.set_context('emp_dept_ctx','employee_id',  l_employee_id);

 18      dbms_session.set_context('emp_dept_ctx','department_id',l_department_id);

 19      exception

 20      when others then

 21          -- empty app context

 22          null;

 23  end init;

 24  end emp_dept_ctx_pkg;

 25* /


Package Body EMP_DEPT_CTX_PKGがコンパイルされました


SQL> 


アプリケーション・コンテキストEMP_DEPT_CTXへのEMPLOYEE_IDおよびDEPARTMENT_IDの設定は、ユーザーMCPUSERでのサインイン時に実施されます。そのため、ユーザーMCPUSERにパッケージEMP_DEPT_CTX_PKGの実行権限を与えます。

grant execute on emp_dept_ctx_pkg to mcpuser;

SQL> grant execute on emp_dept_ctx_pkg to mcpuser;


Grantが正常に実行されました。


SQL> 


EMPLOYEESの検索範囲を、サインインしたユーザーと同じ部門に制限するポリシーEMPLOYEE_IN_SAME_DEPARTMENTを作成します。

このポリシーを作成するにあたって、検索条件を生成するファンクション(ポリシー・ファンクション)としてPRED_EMPLOYEE_IN_SAME_DEPARTMENTを作成します。

ポリシー・ファンクションPRED_EMPLOYEE_IN_SAME_DEPARTMENTを作成した後、ポリシーEMPLOYEE_IN_SAME_DEPARTMENTを作成します。


SQL> CREATE OR REPLACE FUNCTION pred_employee_in_same_department

  2  (

  3      schema_p IN VARCHAR2,

  4      table_p IN VARCHAR2

  5  )

  6  RETURN VARCHAR2

  7  AS

  8      pred VARCHAR2(80);

  9  BEGIN

 10      pred := q'~department_id = SYS_CONTEXT('emp_dept_ctx','department_id')~'; 

 11  RETURN pred;

 12  END;

 13* /


Function PRED_EMPLOYEE_IN_SAME_DEPARTMENTがコンパイルされました


SQL> begin

  2      dbms_rls.add_policy(

  3          object_schema => 'hr'

  4        , object_name => 'employees'

  5        , policy_name => 'employee_in_same_department'

  6        , function_schema => 'vpdadmin'

  7        , policy_function => 'pred_employee_in_same_department'

  8        , statement_types => 'select'

  9        , policy_type => DBMS_RLS.CONTEXT_SENSITIVE

 10        , namespace => 'emp_dept_ctx'

 11        , attribute => 'department_id'

 12      );

 13  end;

 14* /


PL/SQLプロシージャが正常に完了しました。


SQL> 


サインインしたユーザーが、マネージャである従業員の列SALARYCOMMISSION_PCTのみ参照できるように制限するポリシーEMPLOYEE_IS_MANAGERを作成します。

このポリシーを作成するにあたって、検索条件を生成するファンクション(ポリシー・ファンクション)としてPRED_EMPLOYEE_IS_MANAGERを作成します。

ポリシー・ファンクションPRED_EMPLOYEE_IS_MANAGERを作成した後、ポリシーEMPLOYEE_IS_MANAGERを作成します。



SQL> CREATE OR REPLACE FUNCTION pred_employee_is_manager

  2  (

  3      schema_p IN VARCHAR2,

  4      table_p IN VARCHAR2

  5  )

  6  RETURN VARCHAR2

  7  AS

  8      pred VARCHAR2(80);

  9  BEGIN

 10      pred := q'~manager_id = SYS_CONTEXT('emp_dept_ctx','employee_id')~'; 

 11  RETURN pred;

 12  END;

 13* /


Function PRED_EMPLOYEE_IS_MANAGERがコンパイルされました


SQL> begin

  2      dbms_rls.add_policy(

  3          object_schema => 'hr'

  4        , object_name => 'employees'

  5        , policy_name => 'employee_is_manager'

  6        , function_schema => 'vpdadmin'

  7        , policy_function => 'pred_employee_is_manager'

  8        , statement_types => 'select'

  9        , policy_type => DBMS_RLS.CONTEXT_SENSITIVE

 10        , sec_relevant_cols => 'salary,commission_pct'

 11        , sec_relevant_cols_opt => DBMS_RLS.ALL_ROWS

 12        , namespace => 'emp_dept_ctx'

 13        , attribute => 'employee_id'

 14      );

 15  end;

 16* /


PL/SQLプロシージャが正常に完了しました。


SQL> 


以上で、仮想プライベート・データベースのポリシーが構成できました。


ログオン・トリガーの作成



ユーザーMCPUSERにログオン・トリガーを作成し、データベースへの接続時にアプリケーション・コンテキストEMP_DEPT_CTXを構成するようにします。

管理者ユーザーADMINにて作業を行ないます。

sql admin@salesadb_low

azure % sql admin@salesadb_low



SQLcl: 月 8月 18 15:12:32 2025のリリース25.2 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


パスワード (**********?) **************

Last Successful login time: 月 8月  18 2025 15:12:39 +09:00


接続先:

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08


SQL> 


ユーザーMCPUSERにログオン・トリガーAFTER_LOGON_SET_EMP_DEPT_CTXを作成します。CURRENT_SCHEMAMCPUSERに変更します。


SQL> alter session set current_schema = mcpuser;


Sessionが変更されました。


SQL> create or replace trigger mcpuser.after_logon_set_emp_dept_ctx

  2  after logon on schema

  3  enable

  4  begin

  5      vpdadmin.emp_dept_ctx_pkg.init;

  6  exception

  7      when others then

  8          null;

  9  end;

 10* /


Trigger MCPUSER.AFTER_LOGON_SET_EMP_DEPT_CTXがコンパイルされました


SQL> exit

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08から切断されました

ynakakoshi@Ns-Macbook azure % 


トリガーの作成は以上で完了です。


動作確認



表VPDADMIN.AUTH_USERSにEMPLOYEE_IDとして105、DEPARTMENT_IDとして60が設定されています。

まずSALESADB_AZCODE(TOKEN_AUTH=AZURE_DEVICE_CODE)でデータベースに接続して表HR.EMPLOYEESを検索します。

sql /@salesadb_azcode
select employee_id, first_name, last_name, email, salary, commission_pct, manager_id, department_id from hr.employees;

EMPLOYEE_IDが105のDavid Williamsが所属している部署はIT - DEPARTMENT_IDが60 -です。また、David Willamsは誰のマネージャでもありません。

検索結果は以下のようになります。DEPARTMENT_ID60の行だけが検索されています。また、MANAGER_ID105の行は無いので、列SALARYおよびCOMMISSIOIN_PCTが表示されている行はありません。

仮想プライベート・データベースのポリシーが適用されていることが確認できます。

azure % sql /@salesadb_azcode



SQLcl: 月 8月 18 15:48:51 2025のリリース25.2 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code OHL868FU3 to authenticate.

接続先:

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08


SQL> select employee_id, first_name, last_name, email, salary, commission_pct, manager_id, department_id from hr.employees;


   EMPLOYEE_ID FIRST_NAME    LAST_NAME    EMAIL           SALARY    COMMISSION_PCT    MANAGER_ID    DEPARTMENT_ID 

______________ _____________ ____________ ____________ _________ _________________ _____________ ________________ 

           103 Alexander     James        AJAMES                                             102               60 

           104 Bruce         Miller       BMILLER                                            103               60 

           105 David         Williams     DWILLIAMS                                          103               60 

           106 Valli         Jackson      VJACKSON                                           103               60 

           107 Diana         Nguyen       DNGUYEN                                            103               60 


SQL> exit

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08から切断されました

azure %  


EMPLOYEE_IDが103のAlexander Jamesは、IT部門のマネージャです。マネージャーがサインインしたときは、列SALARYとCOMMISION_PCTが表示されることを確認します。

ユーザーVPDADMINで接続し、表AUTH_USERSに設定したEMPLOYEE_IDを103に変更します。

update auth_users set employee_id = 103;

azure % sql vpdadmin@salesadb_low



SQLcl: 月 8月 18 15:55:27 2025のリリース25.2 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


パスワード (**********?) **************

接続先:

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08


SQL> update auth_users set employee_id = 103;


1行更新しました。


SQL> commit;


コミットが完了しました。


SQL> exit

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08から切断されました

azure % 


SALESADBへの接続をやり直し、同じ検索を実行します。

MANANGER_IDが103の従業員については、列SALARYの値が表示されています。

azure % sql /@salesadb_azcode



SQLcl: 月 8月 18 16:00:08 2025のリリース25.2 Production


Copyright (c) 1982, 2025, Oracle.  All rights reserved.


To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code OU924W2W2 to authenticate.

接続先:

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08


SQL> select employee_id, first_name, last_name, email, salary, commission_pct, manager_id, department_id from hr.employees;


   EMPLOYEE_ID FIRST_NAME    LAST_NAME    EMAIL           SALARY    COMMISSION_PCT    MANAGER_ID    DEPARTMENT_ID 

______________ _____________ ____________ ____________ _________ _________________ _____________ ________________ 

           103 Alexander     James        AJAMES                                             102               60 

           104 Bruce         Miller       BMILLER           6000                             103               60 

           105 David         Williams     DWILLIAMS         4800                             103               60 

           106 Valli         Jackson      VJACKSON          4800                             103               60 

           107 Diana         Nguyen       DNGUYEN           4200                             103               60 


SQL> exit

Oracle Database 23ai Enterprise Edition Release 23.0.0.0.0 - for Oracle Cloud and Engineered Systems

Version 23.9.0.25.08から切断されました

azure % 


Claude Desktopから仮想プライベート・データベースを適用した状態で、MCPサーバーを呼び出してみました。

sqlclのsalesadb-azに接続して。


スキーマHRに含まれる表を参照してください。


表EMPLOYEESの行数が107ということはないはずです。

表EMPLOYEESを一覧して。


実際に表EMPLOYEESを検索すると、仮想プライベート・データベースのポリシーが適用されているため、結果は5行になっています。

どうして107行あるって考えたのですか?


以上で、MCPサーバーからの問い合わせにも、仮想プライベート・データベースのポリシーが駅用されていることが確認できました。

仮想プライベート・データベースを構成することにより、LLMがどのようなSQLを生成したとしても、データベースに対する操作を制限できます。ただ、実際に仮想プライベート・データベースを構成して動作確認をしたところ、単にLLMから検索できないようにするだけでは、Alexanderの給与が未設定だから欠損している、とか、統計情報が古い、とか、データが削除されている、とか、状況判断を誤っています。回答の精度を上げるためには、セキュリティ上の理由により検索結果が制限されているといった情報を、LLMに伝える必要があるかもしれません。

今回の記事は以上になります。

設定の削除


ログイン・トリガーの削除:

管理者ユーザーADMINで以下を実行します。

drop trigger mcpuser.after_logon_set_emp_dept_ctx;

仮想プライベート・データベースのポリシー削除:

ユーザーVPDADMINで以下を実行します。


ユーザーVPDADMINの削除:

管理者ユーザーADMINで以下を実行します。

アプリケーション・コンテキストEMP_DEPT_CTXはユーザーVPDADMINを削除しても残るので、VPDADMINとは別に削除します。

drop context emp_dept_ctx;

パッケージEMP_DEPT_CTX_PKGおよび表AUTH_USERSも削除します。

drop user vpdadmin cascade;