MySQL 9.7.0 の新しい LTSリリースでは、Enterprise Edition の新機能の一つとして 動的データマスキング(DDM: Dynamic Data Masking) が追加されました。
MySQL Product Management Director のMike Frankによる最近のブログでは、個人識別情報(PII: Personal Identifiable Information)を扱うあらゆる業界において、DDM がなぜ重要なのかが紹介されています。
Mike の記事では DDM の概要が説明されています。本記事では、MySQL Enterprise Edition 9.7 における動的データマスキング機能のインストール方法、有効化、実際の利用方法 を紹介します。
MySQLにおける動的データマスキングの仕組み
概要:
マスキングは、マスク対象の列を読み取るクエリが実行された時に適用されます。一方で、それらの列への書き込み処理には影響を与えません。この機能は object_policy コンポーネントに依存しており、マスキングポリシー定義を保存・取得します。
クエリ処理中に、マスク対象列への参照は、ポリシーで定義された CASE 式に自動的に置き換えられます。返却される値は、現在のユーザーまたは有効なロールに基づいて決定されます。
セットアップとインストール:
前述のとおり、動的データマスキング は Enterprise Editionの object_policy コンポーネントに依存しています。このコンポーネントは mysql スキーマ内にマスキングポリシーを保存し、ポリシーの作成、管理、取得機能を担っています。
以下の実行例で使用する環境は、 Oracle Linux Server 9.7 上にMySQL Enterprise Edition 9.7.0 の RPM パッケージをデフォルト設定でインストールしています。
Step 1
MySQL パッケージに含まれるインストールスクリプトを実行します。このスクリプトにより、mysql スキーマ内に必要なテーブルが作成され、component_object_policy コンポーネントがインストールされます。
RPM インストールでは通常 /usr/share/mysql-9.7/install_component_object_policy.sql に配置されています。

ファイルの内容は以下のコマンドで確認できます:
cat /usr/share/mysql-9.7/install_component_object_policy.sql

.sql ファイルを実行するには、以下のコマンドを実行します:
mysql -uroot -p < /usr/share/mysql-9.7/install_component_object_policy.sql
MySQL プロンプトから SOURCE コマンドで実行することもできます:
SOURCE install_component_object_policy.sql;
このスクリプトは、ポリシーテーブルを作成し、コンポーネントをインストールします。コンポーネントによって管理されるテーブルは、MySQL 内部で利用するデータベースの一つである mysql 内に column_masking_policies という名前で作成されます。このテーブルの構成は、MySQL にログインして次のコマンドを実行することで確認できます。
use mysql;
show tables like 'column_masking%';

このテーブルの詳細は show create table column_masking_policies\G でも確認できます。

| カラム名 | 役割 |
|---|---|
| policy_name | CREATE、DROP、SHOW、およびカラム割り当てで使用される一意のポリシー識別子 |
| expression | UTF-8 MB4 で保存されるポリシー式 |
| argument | カラムのプレースホルダーとして使用されるポリシーパラメータ名 |
| extra_info | 将来のメタデータ拡張用に予約された JSON フィールド |
| schema_version | アップグレード処理で使用されるコンポーネントテーブルの形式/バージョンマーカー |
Step 2
マスキングコンポーネントをインストールします。マスキング関数 は MySQL Enterprise Edition で提供されている機能です。このマスキング関数を用いてデータをマスクするため、動的データマスキングを利用するには、MySQL プロンプトでマスキングコンポーネントをインストールする必要があります。
INSTALL COMPONENT 'file://component_masking’;
INSTALL COMPONENT 'file://component_masking_functions’;

インストールされたことを確認します。
SELECT * FROM mysql.component;

ID 9 と 10 のコンポーネントが、前の2つのステップでインストールされたものです。
コンポーネントID 1の component_validate_password はデフォルトでインストールされています。
Step 3
次にサンプルデータを作成します:
CREATE SCHEMA IF NOT EXISTS demo_ddm;
USE demo_ddm;
DROP TABLE IF EXISTS employee_details;
CREATE TABLE employee_details (
emp_id INT PRIMARY KEY,
first_name VARCHAR(32),
last_name VARCHAR(32),
ssn VARCHAR(16),
doj datetime,
zip_code VARCHAR(10));
INSERT INTO employee_details VALUES
(1,'James','Smith','900-01-0001','2026-02-04','10001'),
(2,'Mary','Johnson','900-01-0002','2025-04-07','90210'),
(3,'Robert','Williams','900-01-0003','2023-05-07','60601'),
(4,'Patricia','Brown','900-01-0004','2012-05-03','30301');

Step 4
DDM の動作確認のため、MySQL ユーザーとロールを作成します。
実データを参照できる admin ユーザーを作成します。
CREATE USER IF NOT EXISTS 'admin'@'%' IDENTIFIED BY 'Welcome1!';

マスク済みデータのみを参照できる dba_l1 ユーザーを作成します。
CREATE USER IF NOT EXISTS 'dba_l1'@'%' IDENTIFIED BY 'Welcome1!';

両者にSELECT 権限を付与します。
GRANT SELECT ON demo_ddm.employee_details TO 'admin'@'%';
GRANT SELECT ON demo_ddm.employee_details TO 'dba_l1'@'%';

さらにpii_read ロールを作成し、アクセス権を付与します。
CREATE ROLE IF NOT EXISTS 'pii_read'@'%';
GRANT SELECT ON demo_ddm.employee_details TO 'pii_read'@'%';

このロールを admin ユーザーに割り当てます。
GRANT 'pii_read'@'%' TO 'admin'@'%';

Step 5
マスキングポリシーを作成操作に移る前に、マスキングポリシーとは何か、どのように作成するかを確認しましょう。
マスキングポリシーは、ユーザーレベルでも ロールレベルでも作成できます。実行例では ユーザーレベルで作成し、ロール レベルでの作成は後ほど説明します。
ユーザーレベルで マスキングポリシー を作成する DDL 構文は次のとおりです:
CREATE MASKING POLICY IF NOT EXISTS <policy_name>(<policy_param>)
CASE WHEN CURRENT_USER_IN('<role_list/user_list>')
THEN <policy_param>
ELSE <masking_expression_using_policy_param>
END;
| 要素 | 説明 |
|---|---|
| policy_name | 再利用可能なポリシー名。空文字、末尾スペースはサポートしておらず、64文字を超えてはいけません。名前の照合は utf8mb3_general_ci のもとで大文字小文字を区別しません。SHOW CREATE MASKING POLICY は元の大文字小文字を保持します。 |
| policy_param | 対象列の値を表す単一のパラメータ。有効な識別子であり、空文字、末尾スペースはサポートしておらず、64文字を超えてはいけません。大文字小文字を区別しません。 |
| CURRENT_ROLE_IN(…) | 有効なロールのいずれかが認可リストに一致する場合に true を返すゲートキーパーです。 |
| CURRENT_USER_IN(…) | 現在のユーザーが認可リストに一致する場合に true を返すゲートキーパーです。 |
| role_list / user_list | カンマ区切りの認可 ID です。CREATE MASKING POLICY では、引数は文字列リテラルである必要があります。 |
| THEN / ELSE | 一方の分岐は元のパラメータを直接返し、もう一方の分岐はマスクされた値を返す必要があります。 |
| IF NOT EXISTS | 大文字小文字を区別しない一致ポリシーがすでに存在する場合、作成は成功しますが効果はなく、警告が出力されます。既存の定義は保持されます。 |
マスキングポリシーのサンプルを作成します。
CREATE MASKING POLICY mask_ssn_policy(ssn_col)
CASE WHEN CURRENT_USER_IN('admin')
THEN ssn_col
ELSE mask_ssn(ssn_col)
END;

ここでは、mask_ssn_policy というポリシーを定義しています。admin ユーザーがログインした場合は実際の 社会保障番号(SSN: Social Security Number) が表示され、それ以外のユーザーでは 社会保障番号がマスクされた状態で表示されます。
このポリシーを demo_ddm.employee_details テーブルに関連付けます。
ALTER TABLE demo_ddm.employee_details
ALTER COLUMN ssn
SET MASKING POLICY mask_ssn_policy;

ここでは、employee_details テーブルの ssn カラムに、マスキングポリシー mask_ssn_policy を関連付けています。
ログインしているユーザーが admin の場合:
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

ログインしているユーザーが root または dba_l1 の場合:
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

次に、名前カラム用の別のポリシーを作成します。
CREATE MASKING POLICY mask_name_policy(name_col)
CASE WHEN CURRENT_USER_IN('admin')
THEN name_col
ELSE mask_outer(name_col, 1,1)
END;

ユーザーが admin の場合、名前はそのまま表示されます。それ以外の場合は、先頭・末尾文字が X に置き換えられます。
ポリシーをテーブルと対象カラムに関連付けます。
ALTER TABLE demo_ddm.employee_details
ALTER COLUMN first_name SET MASKING POLICY mask_name_policy,
ALTER COLUMN last_name SET MASKING POLICY mask_name_policy;

admin ユーザーでログインします。
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

root ユーザーでログインします。
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

次に dba_l1 ユーザーで確認します。
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

rootユーザでログインし、employee_details テーブルから SSN のマスキングを解除します。
ALTER TABLE demo_ddm.employee_details MODIFY COLUMN ssn varchar(16) NOT NULL;

pii_read ロールへのポリシーを作成します。
CREATE MASKING POLICY mask_name_forRole(name_col)
CASE WHEN CURRENT_ROLE_IN('pii_read')
THEN name_col
ELSE mask_outer(name_col, 1,1)
END;

ポリシーをテーブルに関連付けます。
ALTER TABLE demo_ddm.employee_details
ALTER COLUMN first_name
SET MASKING POLICY mask_name_forRole;

admin ユーザーは pii_read ロールに関連付けられてますが、まだ有効化されていません。この状態でadmin でログインして、何が表示されるかを確認します。
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

admin ユーザーでログインしていますが、有効化されていないため first_name 列にはマスクされたデータのみが表示されています。
admin ユーザーでROLE を有効化します。
SET ROLE pii_read;

次に 再度 SELECT 文を実行します。
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

ここでは、マスクされていないデータを確認できます。ロールに対して設定すると完全なデータを表示できますが、単にユーザーとして接続しているだけの場合は、マスクされたデータが表示されます。
マスキングコンポーネントのアンインストール
Linux マシンの同じフォルダ /usr/share/mysql-9.7/ にデフォルトで存在する uninstall_component_object_policy.sql スクリプトを実行します。
mysql -uroot -p -h localhost < /usr/share/mysql-9.7/uninstall_component_object_policy.sql
MySQL にログインし、demo_ddm.employee_details テーブルに対して SELECT を実行すると、エラーが発生することを確認します。
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

テーブルから マスキングポリシーを解除します。
ALTER TABLE demo_ddm.employee_details MODIFY COLUMN ssn varchar(16) NOT NULL;
ALTER TABLE demo_ddm.employee_details MODIFY COLUMN first_name VARCHAR(32);
ALTER TABLE demo_ddm.employee_details MODIFY COLUMN last_name VARCHAR(32);

次に SELECT 文を実行します。
SELECT emp_id, first_name, last_name, ssn, doj, zip_code
FROM demo_ddm.employee_details
ORDER BY emp_id;

MySQL にインストールされているコンポーネントを表示し、component_object_policy が削除されていることを確認します。(マスキングコンポーネントを削除する場合は別途アンインストールする必要があります)
select * from mysql.component;

まとめ
この機能はサーバー側で強制されるように設計されているため、MySQL に直接アクセスした場合でも、データの不適切な取り扱いを避けることができます。
クエリ時、かつサーバーレベルで適用されるため、DWH、CRM、AI、レポーティングなど、他の用途のためにデータを抽出する場合にも利用できます。
オリジナルのデータは MySQL 内に保持され、安全に保護されます。その一方で、他の用途にはマスク済みデータを抽出できます。
詳細情報 / OCI HeatWaveで試す
