17
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Select AI × Data Redactionで叶える“見せない”自然言語アクセス

Posted at

🏁 はじめに

前回の記事「Select AI × VPDで実現するセキュアな自然言語アクセス」では、部門ごとの行レベル制御をVPD(Virtual Private Database)で実現する方法を紹介しました。
今回はその続編として、列の中身(値)を動的にマスクする Data Redaction にフォーカスします。
Oracle Database 23ai の Select AI 機能と組み合わせることで、
自然言語での問い合わせでも個人情報をDB側で安全に保護する方法を解説します。

Data Redactionとは❓️

アプリケーションやSQLクエリの修正を行わずに、データベース側で値を自動的にマスク(隠蔽) する機能です。
たとえば、社員テーブルの電話番号カラムに対して、
「人事部以外のユーザーは電話番号を見せない」というルールを定義しておくと、
SELECT文の結果が自動的にマスクされます。

Data Redactionの例
select ai 社員の電話番号教えて;

-- 結果
| 社員名   | 電話番号      |
|-----------|----------------|
| 佐藤 花子 | ***-****-**** |
| 高橋 大輔 | ***-****-**** |

特徴としては以下の点が挙げられます。

✅ SQLやアプリ側の修正が不要
アプリが通常通りSelect文を実行しても、DBが自動でマスク結果を返す。

✅ 実データはそのまま保持される
物理データは変更されず、セッション/ユーザーごとに論理的にマスキング。

✅ 柔軟なマスキングルール指定が可能
正規表現・ランダム文字・部分マスクなど多様な function_type により制御。

つまり、Data Redactionは「見せない」というよりはむしろ「見せ方を変える」制御です。
同じテーブルに対しても、ユーザー属性(SYS_CONTEXT)やアプリコンテキストに応じて
動的に出力を変更できる点が非常に強力です。

Data Redactionの各パターン

🔖 function_type 一覧表はこちら⬇️

function_type no. function_type マスキング内容 出力例 (090-1234-5678) 主な用途
0 DBMS_REDACT.NONE 変更なし(マスキング無効) 090-1234-5678 テスト・確認用
1 DBMS_REDACT.FULL 全桁に空文字指定 `` 完全非表示
2 DBMS_REDACT.PARTIAL 一部だけマスク(入力/出力フォーマット指定) 090-XXXX-XXXX 先頭のみ見せる、電話番号・カード番号など
4 DBMS_REDACT.RANDOM 乱数/英数記号などのランダム文字に置換(毎回異なる) e:3(NhmhB;0 本番データを見せずに形式だけ維持したいとき
5 DBMS_REDACT.REGEXP 正規表現にマッチした数字を任意文字(例: *)に置換 ***-****-**** 柔軟に文字列パターンを処理したい場合
7 DBMS_REDACT.REGEXP_WIDTH REGEXP と同様だが列の文字幅を保持 ***-****-**** OCI/OLE DB 経由アプリとの互換性維持
6 DBMS_REDACT.NULLIFY 値を NULL として返却 (NULL) 空欄扱いにしたい場合(非表示化)

Data Redactionポリシー指定方法

実行ユーザにData Redaction実行権限を設定
GRANT EXECUTE ON DBMS_REDACT TO SELECT_AI_USER;

アプリケーションコンテキストを適宜、既存認証機構に合わせて設定します。
参考

-- 開発部のユーザ
BEGIN
  select_ai_user.pkg_emp_ctx.set_dept('開発部');
END;

Data Redactionのマスキングタイプごとの設定方法は下記です:

1️⃣ NONE(マスクなし)

BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema   => 'SELECT_AI_USER',
    object_name     => 'EMPLOYEE_PRIVATE',
    policy_name     => 'REDACT_PHONE',
    column_name     => 'PHONE',
    function_type   => DBMS_REDACT.NONE,
    expression      => 'SYS_CONTEXT(''EMP_CTX'',''DEPT'') <> ''人事部'''
  );
END;
/

2️⃣ FULL(完全マスク)

BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema   => 'SELECT_AI_USER',
    object_name     => 'EMPLOYEE_PRIVATE',
    policy_name     => 'REDACT_PHONE',
    column_name     => 'PHONE',
    function_type   => DBMS_REDACT.FULL,
    expression      => 'SYS_CONTEXT(''EMP_CTX'',''DEPT'') <> ''人事部'''
  );
END;
/

3️⃣ PARTIAL(部分マスク)

BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema        => 'SELECT_AI_USER',
    object_name          => 'EMPLOYEE_PRIVATE',
    policy_name          => 'REDACT_PHONE',
    column_name          => 'PHONE',
    function_type        => DBMS_REDACT.PARTIAL,
    function_parameters  => 'VVVFVVVVFVVVV,VVV-VVVV-VVVV,X,4,11',
    expression           => 'SYS_CONTEXT(''EMP_CTX'',''DEPT'') <> ''人事部'''
  );
END;
/

4️⃣ RANDOM(ランダム値に置換)

BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema   => 'SELECT_AI_USER',
    object_name     => 'EMPLOYEE_PRIVATE',
    policy_name     => 'REDACT_PHONE',
    column_name     => 'PHONE',
    function_type   => DBMS_REDACT.RANDOM,
    expression      => 'SYS_CONTEXT(''EMP_CTX'',''DEPT'') <> ''人事部'''
  );
END;
/

5️⃣ REGEXP(正規表現マスク)

BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema          => 'SELECT_AI_USER',
    object_name            => 'EMPLOYEE_PRIVATE',
    policy_name            => 'REDACT_PHONE',
    column_name            => 'PHONE',
    function_type          => DBMS_REDACT.REGEXP,
    regexp_pattern         => '[0-9]',
    regexp_replace_string  => '*',
    expression             => 'SYS_CONTEXT(''EMP_CTX'',''DEPT'') <> ''人事部'''
  );
END;
/

6️⃣ REGEXP_WIDTH(正規表現+文字幅維持)

REGEXPと指定方法は同じなため省略

7️⃣ NULLFY(NULL)

BEGIN
  DBMS_REDACT.ADD_POLICY(
    object_schema   => 'SELECT_AI_USER',
    object_name     => 'EMPLOYEE_PRIVATE',
    policy_name     => 'REDACT_PHONE',
    column_name     => 'PHONE',
    function_type   => DBMS_REDACT.NULLIFY,
    expression      => 'SYS_CONTEXT(''EMP_CTX'',''DEPT'') <> ''人事部'''
  );
END;
/

Data Redactionポリシー確認&削除

ポリシー確認
SELECT * FROM REDACTION_POLICIES;  
ポリシー削除
BEGIN
  DBMS_REDACT.DROP_POLICY(
    object_schema => 'SELECT_AI_USER',
    object_name   => 'EMPLOYEE_PRIVATE',
    policy_name   => 'REDACT_PHONE'
  );
END;
/

🤖 Select AI × Data Redaction

Select AI と Data Redaction を組み合わせることで、
自然言語からの問い合わせに対しても 安全に個人情報をマスキング した結果を返すことができます。

たとえば、以下のように「社員の電話番号を教えて」と問い合わせた場合を考えてみます。

電話番号はマスキング
select ai 社員の電話番号教えて;

-- 結果
| 社員名   | 電話番号      |
|-----------|----------------|
| 佐藤 花子 | ***-****-**** |
| 高橋 大輔 | ***-****-**** |

このように、実データは保持したまま、出力時に自動でマスキング処理が適用されます。
アプリやAIのクエリロジックを一切変更せず、データベースレイヤーだけで実現できるのが大きな特徴です。

🗣️ AIが自然に「非公開」と応答する

Select AI の「narrate」モードを使うと、AIが結果を自然言語で説明します。
Data Redactionによるマスク結果を認識して、次のような“人間らしい”回答を返します。

生成AIは"非公開"と返答
select ai narrate 社員の電話番号教えて;

-- 結果
- 佐藤 花子さんの電話番号は非公開です。
- 高橋 大輔さんの電話番号は非公開です。

つまり、AIは「値が存在しない」のではなく「マスクされている」ことを理解できるため、
「情報非公開です」と自然に返答します。
この点が、Select AI と Data Redaction の相性の良さを象徴しています。

💡 Data Redactionを選ぶべきシーン

Data Redaction は以下のようなケースで特に効果を発揮します:

  • 🧱 アプリやBIツールとの互換性を維持したい(列構造を壊さず、NULLを避けたい)
  • 🤖 Select AI / LLM で自然な返答を維持したい(「データがない」ではなく「非公開」とLLMにより解釈させたい)
  • 🧩 テスト・開発環境で形式を維持したダミーデータを使いたい
  • 🧮 特定条件(部署・職位・時間帯など)で部分的にマスクしたい

✳️ まとめ

VPDが「アクセスそのものを遮断するドアロック」だとすれば、
Data Redactionは「見せるが中身は隠すすりガラス」です。

両者は排他的ではなく、補完的に利用できる関係にあります。
VPDで「どの行・列を見せるか」を制御し、
Data Redactionで「見せる際の中身(値)をどう見せるか」を制御することで、
よりきめ細かく、かつユーザー体験を損なわないセキュリティを実現できます。

特に Select AI のように自然言語でデータを返す機能では、
AIが「データが存在しない」のか「非公開なのか」を正しく理解できるようにすることが重要です。
Data Redactionはその文脈を保ちつつ、個人情報を安全に扱うことを可能にします。

🧩 VPD × Data Redaction × Select AI
行・列・値という3層の視点で制御を組み合わせることで、
“セキュリティと使いやすさを両立したAIデータアクセス”を構築できます。

📚 参考リンク

Oracle — DBMS_REDACT リファレンスガイド
Oracle — Data Redaction ポリシー管理と権限設定
Qiita — Select AI × VPDで実現するセキュアな自然言語アクセス

17
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?