データベースのアクセス制御では、利用者やアプリケーションに対して、表の参照や変更を許可するかどうかをオブジェクトを最小単位として権限で制御します。
しかし、表へのSELECT権限を持つ利用者であっても、その表のすべての行を見せてよいとは限りません。
たとえば、次のような例を考えると、同じ表を複数の利用者やテナントで共有する場合は、利用者ごとに参照または変更できる行を分ける必要があります。
- 自分が担当する顧客の行だけを参照できる
- 自社テナントの行だけを参照または変更できる
- 契約中の案件の行だけを処理できる
このような場合、アプリケーションではログインした利用者のロール、所属部門、テナントID、データの所有者などを判定し、SQLのWHERE句、JOINや、API・サービス層の認可ロジックで返すデータを絞り込むことが一般的です。
この設計は多くのアプリケーションで使われていますが、行の絞り込みをアプリケーションだけに任せる場合、制御の有効性は画面、API、バッチ処理など、それぞれの実装に依存します。

結果として、条件の記述漏れがあったり、想定していない経路から表へアクセスできたりすると、アプリケーション側の制御だけでは意図しない行が処理対象になるリスクがあります。

この行レベルの制御をアプリケーションの実装だけに任せず、データベース側でも強制する機能が、Oracle Virtual Private Database(VPD) です。
VPDを設定した表などへアクセスすると、Oracle Databaseは利用者やテナントに応じた条件を自動的に適用します。
同じデータへ複数のアプリケーションや接続方法からアクセスする場合でも、共通のルールで処理対象の行を制限できます。

この記事では、そんなアクセス制御機能としてVPDを紹介します。
1. VPD とは
Oracle Virtual Private Database(VPD) は、利用者やアプリケーションの条件に応じて、表、ビュー、シノニムのどの行を処理対象にするかを制御する、Oracle Database の機能です。
VPDは行レベル・アクセス制御の一つであり、同じ表に対する同じSQLでも、接続している主体やセッションの情報によって異なる結果を返すことが可能です。
たとえば、一つの注文表を複数の企業が共有する場合を考えます。
VPDを設定すると、企業Aの利用者には企業Aの注文だけを、企業Bの利用者には企業Bの注文だけを返すようにできます。同様の考え方で、担当者、所属部門、地域、契約状態などの業務条件にも適用することが可能です。
この仕組みとしては、VPDではポリシー関数が返す条件(述語)をOracle DatabaseがSQLへ動的に追加したものとして実行します。そのため、アプリケーションが個々のSQLに条件を書かなくても、条件に合わない行を検索結果から除外することができます。
また、ポリシーの対象とするSQL操作を指定すれば、INSERT、UPDATE、DELETEに対しても行の制御を適用が可能です。

2. VPD の構成要素
VPDは、保護対象に適用するVPDポリシーと、アクセス条件を返すポリシー関数の二つで構成されます。
| 要素 | 役割 |
|---|---|
| ポリシー関数 | 現在のセッション情報などを参照し、適用する条件(述語)を文字列として返す関数 |
| VPDポリシー | ポリシー関数を保護対象の表、ビュー、またはシノニムへ関連付け、適用するSQL操作を指定する |
ポリシー関数
ポリシー関数は、VPDがアクセス条件を生成するために使うPL/SQL関数です。
関数は対象スキーマ名とオブジェクト名を引数に受け取り、適用する条件(述語)を文字列で返します。
そのため、ポリシー関数を作成しただけでは表に制御は適用されません。
ここでは、利用者が個別のデータベース・ユーザーで接続し、表のASSIGNED_USER列に担当者のデータベース・ユーザー名を格納している場合を考えます。
この場合、以下のようなポリシー関数となります。
CREATE OR REPLACE FUNCTION SALES.ORDERS_USER_PREDICATE (
p_schema IN VARCHAR2,
p_object IN VARCHAR2
) RETURN VARCHAR2
AS
BEGIN
RETURN q'[ASSIGNED_USER = SYS_CONTEXT('USERENV', 'SESSION_USER')]';
END;
/
この関数では、Oracle Databaseが提供するUSERENV名前空間から現在のデータベース・ユーザーを取得し、述語として値を返却しています。
この関数が返す述語により、ASSIGNED_USER列が現在のデータベース・ユーザー名と一致する行だけが処理対象になります。
※ SYS_CONTEXT('USERENV', 'SESSION_USER')は、現在のセッションへログインしたデータベース・ユーザー名を返します。
また、ポリシー関数では必要に応じて条件分岐を行い、利用者や処理に応じて異なる述語を返すこともできます。
VPDポリシー
VPDポリシーは、上記のポリシー関数を保護対象とする表、ビュー、またはシノニムへ関連付けます。
例えば、以下はVPDポリシーを追加する文の例ですが、ここで対象オブジェクト、ポリシー関数、適用するSQL操作を指定します。
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'SALES',
object_name => 'ORDERS',
policy_name => 'ORDERS_USER_POLICY',
function_schema => 'SALES',
policy_function => 'ORDERS_USER_PREDICATE',
statement_types => 'SELECT'
);
END;
/
この例では行レベルの制御を設定していますが、VPDは特定の列を対象とする制御にも対応しています。DBMS_RLS.ADD_POLICYのsec_relevant_colsに列名を指定すると、その列がSQLから参照された場合だけポリシーを適用できます。
さらに、sec_relevant_cols_opt => DBMS_RLS.ALL_ROWSを指定したSELECTでは、すべての行を返しながら、ポリシーの条件を満たさない行の対象列をNULLとして返せます。
本記事の以降は、VPDの基本となる行レベルの制御を中心に説明します。
3. SQLに条件が追加される流れ
前節のポリシー関数とVPDポリシーを使い、アプリケーションがSQLを実行してから結果を受け取るまでの流れを確認します。
まず、アプリケーションがSALES.ORDERS表へ次のSQLを送信します。
SELECT order_id, customer_name, amount
FROM sales.orders
WHERE status = 'OPEN';
このSQLには、担当者を限定する条件がありません。
しかし、SALES.ORDERS表にはVPDポリシーが設定されているため、Oracle Databaseはポリシー関数が返す次の述語を適用します。
ASSIGNED_USER = SYS_CONTEXT('USERENV', 'SESSION_USER')
Oracle Databaseは、元のSQLにこの述語を追加したものとして評価します。
元のSQLにはすでにWHERE句があるため、概念的には次のようにAND条件が追加されます。
SELECT order_id, customer_name, amount
FROM sales.orders
WHERE status = 'OPEN'
AND ASSIGNED_USER = SYS_CONTEXT('USERENV', 'SESSION_USER');
この処理はOracle Database内で行われるため、アプリケーションが元のSQLを書き換える必要はありません。
アプリケーションには、STATUSがOPENで、かつASSIGNED_USERが現在のデータベース・ユーザー名と一致する行だけが返ります。
ただし、複数の利用者が同じデータベース・ユーザーを共有する構成では、SESSION_USERだけで実際の利用者を区別できません。
共有アカウントや接続プールで実際の利用者をVPDの判定へ反映する場合は、次の節にて説明します。
4. VPDの判定に使う利用者情報
VPDはVPD関数という形で PL/SQLで条件を記述することになります。
上記の例では、セッションに保持された情報をSYS_CONTEXT関数で参照し、行を制御する条件の判断に使用していましたが、その他条件として使用できる情報としてはどのようなものが使えるのでしょうか。
この記事では、利用できる情報として、データベースが提供するもの、アプリケーションが設定するもの、APEXやMCPサーバーなどの連携機能が伝えるものに分けて解説します。
4-1. データベースが提供するコンテキスト
USERENVには、ログインしたデータベース・ユーザー、認証方式、接続先、クライアント情報など、現在のセッションに関する属性が用意されています。
以下ドキュメントリンクでは利用できる属性と戻り値を見ることができます。
https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/SYS_CONTEXT.html
代表的な属性は次のとおりです。
| 分類 | 属性 | 主な用途 |
|---|---|---|
| ログインと認証 | SESSION_USER、AUTHENTICATED_IDENTITY、AUTHENTICATION_METHOD、PROXY_USER | データベースが認識している接続主体と認証方式 |
| 接続先 | SERVICE_NAME、CON_NAME、CDB_NAME | 接続サービスやコンテナ |
| 接続元 | IP_ADDRESS、CLIENT_PROGRAM_NAME、OS_USER | 接続経路やクライアント |
| アプリケーション識別 | CLIENT_IDENTIFIER、MODULE、ACTION、CLIENT_INFO | 監視や追跡に使うための、共有セッション内の利用者や処理 |
SESSION_USERは通常、ログインしたデータベース・ユーザーを示しますが、共有アカウントの背後にいる実ユーザーまでは識別できないことに注意します。
また、CLIENT_IDENTIFIERはDBMS_SESSION.SET_IDENTIFIER、MODULEとACTIONはDBMS_APPLICATION_INFOを使用してアプリケーションから動的に設定することが可能です。
4-2. 独自のアプリケーション・コンテキスト
業務上で使用されるテナントIDや担当部門などの独自のコンテキストは、通常のUSERENVには含まれません。
このような属性は、アプリケーション専用の名前空間を作り、独自のアプリケーション・コンテキストとして保持および使用することができます。
CREATE CONTEXTでは、通常のセッション・コンテキストについて、指定したパッケージから値設定について説明されていますので参考にしてください。
また、接続プールでは、一つのデータベース・セッションが別の利用者へ再利用されます。
利用者が切り替わるたびにコンテキストを消去または再設定しないと、前の利用者の属性が残る可能性があります。そのため、DBMS_SESSION.CLEAR_CONTEXTやCLEAR_IDENTIFIERによる消去を行う必要があることにも注意します。
4-3. 連携サービスが固有に持つコンテキスト
Oracle が提供するローコードのアプリケーション開発ツール「Oracle APEX」や、OCI で提供されるマネージドMCPサーバー「Database Tools MCP Server」や「ADB MCP Server」では、固有の名前空間にセッションコンテキストを持っており、これをVPDの判断材料として使用することが可能です。
APEX
Oracle APEXは受信したリクエストの処理を開始するとき、APEX$SESSION名前空間へAPEXセッションの情報を設定します。
ドキュメントでは、以下リンクよりVPDのポリシー関数から次の属性を参照できます。
https://docs.oracle.com/en/database/oracle/apex/26.1/apxdc/referencing-other-context-information.html
| 属性 | 内容 |
|---|---|
APP_USER | 現在のAPEX利用者 |
APP_ID | アプリケーションID |
APP_SESSION | APEXセッションID |
APP_TENANT_ID | アプリケーションが設定したテナントID |
DB Tools MCP サーバー
OCI では、マネージド MCP サーバーとして以下の2つが現在展開されており、これらMCPサーバーサービスでは、コンテキストが自動で付与されます。これを用いることで、AIエージェントに限定したアクセス制御を効かせることも可能です。
| サービス | 名前空間 | 主な属性 | ドキュメントリンク |
|---|---|---|---|
| OCI Database Tools MCP Server | CLIENTCONTEXT | OAUTH_SUB、OAUTH_SUB_TYPE、IAM_DOMAIN_APP_ROLES、RESOURCE_OCIDなど | https://docs.oracle.com/ja-jp/iaas/database-tools/doc/database-identity-propagation.html |
| Autonomous AI Database MCP Server | MCP_SERVER_CONTEXT$ | USER_IDENTITY | https://docs.oracle.com/ja-jp/iaas/autonomous-database-serverless/doc/mcp-server-concepts.html |
5. Oracle Label Securityとの違い
前回の記事にて紹介した Oracle Label Security は、VPDと類似のアクセス制御を行うものとなります。
この2つの機能の違いとして、以下に表としてまとめてみます。
| 機能 | 主な制御単位 | 制御基準 | 設定 | 向いている要件 | ライセンス |
|---|---|---|---|---|---|
| VPD | 行、または特定の列 | SQLに動的に追加される述語 | ・PL/SQLでwhere句の述語部分をコーディングする必要がある | 顧客ごとのデータ隔離、時間帯によるアクセス制限 | Enterprise Edition に無償で付属 |
| Oracle Label Security | 行 | データの機密性に応じたラベル | ・各データに対し、ラベルを設定する必要がある ・Enterprise Manager にてGUIで設定が可能 | 「公開」「社内限定」「機密」のような分類体系の制御 | Enterprise Edition の追加セキュリティオプション |
6. セキュリティと運用上の注意点
VPDによるアクセス制御の強制力は、ポリシー関数が参照するコンテキストの信頼性に依存します。
利用者や業務属性を誰が設定し、どの経路から変更できるかをポリシー作成前に決めることが重要です。
- コンテキストがない場合は拒否する
- ポリシー関数が
NULLまたは空の述語を返すと、行は制限されません。 - 必要なコンテキストが未設定、不正、または期限切れの場合は、
1=2のように行を返さない述語を返すか、SQLを失敗させます。 - 許可条件を確認できた場合だけ対象行を返す動作にします。
- ポリシー関数が
- 管理者の除外経路を別に統制する
- VPDは
SYSユーザー、AS SYSDBAなどの管理権限を用いた接続、またはEXEMPT ACCESS POLICYを持つ主体には適用されません。 - 標準の
DBAロールもVPDポリシーを変更または削除でき、SYSスキーマのオブジェクトやTRUNCATE、ALTER TABLEなどのDDLもVPDでは保護できません。 - そのため、強権限ユーザーの操作は、Database Vaultや統合監査などの別の統制で補うこととなります
- VPDは
7. ライセンスについて
VPD を利用する場合は、Enterprise Edition 以上にて追加ライセンスなく利用することが必要です。
OCI なども含めると、Oracle Label Security が利用できる環境は以下のとおりです。
https://oracleapex.com/ords/features/r/dbfeatures/licenses?license_id=102
- Oracle AI Database FREE
- Enterprise Edition
- Oracle Base Database Service Enterprise Edition
- Oracle Base Database Service Enterprise Edition – High Performance
- Exadata Database Service on Dedicated Infrastructure / Cloud@Customer
Oracle AI Database FREE でも利用できるため、機能検証や簡単な動作確認は無償環境で始めることができます。
一方で、バージョンによって利用可能な機能に違いがありますので、必要に応じて確認しておきましょう。
https://oracleapex.com/ords/r/features/dbfeatures/home
8. まとめ
VPDを使うと、同じ表を共有する利用者やテナントに対し、それぞれに許可された行だけを参照または変更できるように制御できます。
この制御を保護対象へ関連付けるため、複数のアプリケーションや接続方法からアクセスする場合でも、データベース側で共通のルールを適用できます。
アプリケーションの検索条件とVPDは、どちらか一方だけを選ぶ関係ではありません。
アプリケーションは画面や処理に必要なデータを取得し、VPDは利用者が許可された範囲を超えないように制御します。
またVPDでは、セッションから伝播する属性のうち、どの値を誰が設定し、どの接続経路で信頼できるかを設計して初めて、業務上の認可を行の条件へ変換することになります。そのため、SQL自体は透過的な処理になりますが、設計自体はアプリケーションまで含めて考えることが重要です。
次回は、Oracle AI Database Free上で独自のアプリケーション・コンテキストとVPDポリシーを作り、実際の動作を確認してみます。