18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PostgreSQLAdvent Calendar 2019

Day 16

PostgreSQL Anonymizer でデータの匿名化・マスキングをしてみた

Last updated at Posted at 2019-12-15

この記事は PostgreSQL Advent Calendaer 2019 の 16 日目です。昨日の記事は @masayuki038@github さんの「PostgreSQL の JIT が生成するコードを眺めてみる」でした。この記事では PostgreSQL でデータの匿名化・マスキングを行う拡張 PostgreSQL Anonymizer について紹介します。

匿名化・マスキングとは

データベースには個人情報や企業の機密情報など、取り扱いに注意が必要なデータが含まれています。そのようなデータを開発やテスト、分析でそのまま使っては情報漏洩のリスクがあります。そのような場合にデータを安全に取り扱う方法の 1 つに匿名化があります。

匿名化 (anonymization) はデータから個人を特定できる情報を取り除くことです。マスキング (masking) は匿名化の一種で、情報を隠して匿名化することです。ただ、匿名化とマスキングはほぼ同じ意味で使われることが多いようです。

PostgreSQL Anonymizer とは

PostgreSQL Anonymizer は PostgreSQL でデータの匿名化・マスキングを行う拡張です。開発元はフランスの Dalibo 社です。以前は Ora2Pg の開発者がいたことでも有名でした。ライセンスは PostgreSQL と同じく PostgreSQL ライセンス です。

PostgreSQL Anonymizer では、PostgreSQL のデータ定義言語 (DDL) を拡張し、データをどのようにマスキングするかのルールを定義します。それにより、データをマスキングして更新したり、SQL 形式でダンプしたり、ユーザにマスキングしたデータをアクセスさせたりできます。

PostgreSQL Anonymizer は PostgreSQL 9.6 以降に対応しています。バージョン 9.5 でも使えなくはないですが、パッケージが提供されておらず、いくつか問題 もあります。また、まだ開発途上にあるプロジェクトなので、それを分かった上で使うのがいいでしょう。

PostgreSQL Anonymizer のインストール

PostgreSQL Anonymizer は PostgreSQL 開発元の Yum リポジトリに含まれているので、CentOS などの Red Hat 系 OS を使っていて、Yum リポジトリを登録済みであれば、簡単にインストールできます。PostgreSQL のインストールについては こちらの記事 を参考にしてください。

ここでは、CentOS 8 に PostgreSQL 12 をインストールした状態を前提にします。

まず、PostgreSQL Anonymizer のパッケージ postgresql_anonymizer12 をインストールします。パッケージ名の最後の数字 12 は PostgreSQL のバージョンを表します。お使いの PostgreSQL のバージョンに合わせて変更してください。

# yum -y install postgresql_anonymizer12
(省略)
Installed:
  postgresql_anonymizer12-0.5.0-1.rhel8.x86_64
  perl-Carp-1.42-396.el8.noarch
  perl-Exporter-5.72-396.el8.noarch
  perl-libs-4:5.26.3-416.el8.x86_64
  ddlx_12-0.15-1.rhel8.noarch
  postgresql12-contrib-12.1-2PGDG.rhel8.x86_64

Complete!

postgresql_anonymizer12 パッケージをインストールすると、依存関係で perl 関連、ddlxpostgresql12-contrib パッケージがインストールされます。ddlx パッケージはデータベースオブジェクトから DDL を抽出する拡張です。postgresql12-contrib パッケージは PostgreSQL の追加モジュールで、必要なのはそれに含まれる拡張 tsm_system_rows です。tsm_system_rows 拡張は最大行数を指定してテーブルからサンプリングする拡張です。perl 関連パッケージは追加モジュールの一部が必要としています。

次に、PostgreSQL の設定ファイル postgresql.conf を編集し、PostgreSQL サーバの起動時に読み込む共有ライブラリを指定するパラメータ shared_preload_libraries に PostgreSQL Anonymizer を表す anon をカンマ区切りで追加します。

$PGDATA/postgresql.conf
# - Shared Library Preloading -

shared_preload_libraries = 'anon'       # (change requires restart)
#local_preload_libraries = ''
#session_preload_libraries = ''
#jit_provider = 'llvmjit'               # JIT library to use

パラメータの設定を反映させるため、PostgreSQL のサービスを再起動します。

# systemctl restart postgresql-12.service

最後に、PostgreSQL Anonymizer のテスト用にデータベース anondb を作成して接続し、PostgreSQL Anonymizer の拡張 anon をインストールし、anon.load 関数でマスキングに使う偽装データをロードします。

# su - postgres
Last login: Tue Dec  3 21:10:31 UTC 2019 on pts/0
$ createdb anondb
$ psql anondb
=# CREATE EXTENSION anon CASCADE;
NOTICE:  installing required extension "tsm_system_rows"
NOTICE:  installing required extension "ddlx"
CREATE EXTENSION
=# SELECT anon.load();
SELECT
 load
------
 t
(1 行)

=# \q

CREATE EXTENSION コマンドの実行時に CASCADE を指定するのは、依存関係のある拡張をいっしょにインストールするためです。

これで、PostgreSQL Anonymizer のインストールは完了です。

マスキングルールの定義

テストデータを生成し、マスキングルールを定義します。

まず、テストデータを生成します。部署と社員情報を格納するテーブル departmentsemployees を作成し、データを登録する SQL ファイル init.sql を用意します。

init.sql
CREATE TABLE departments (
    id serial PRIMARY KEY,
    name text NOT NULL
);

CREATE TABLE employees (
    id serial PRIMARY KEY,
    last_name text NOT NULL,
    first_name text NOT NULL,
    email text NOT NULL,
    salary int NOT NULL,
    department_id int NOT NULL REFERENCES departments (id),
    birth date NOT NULL
);

INSERT INTO departments (name) VALUES
    ('営業部'),
    ('技術部');

INSERT INTO employees (last_name, first_name, email, salary, department_id, birth) VALUES
    ('佐藤', '太郎', 'sato@example.com', 300000, 1, '1987-01-02'),
    ('鈴木', '次郎', 'suzuki@example.com', 300000, 2, '1988-03-04'),
    ('高橋', '三郎', 'takahashi@example.com', 350000, 1, '1989-05-06'),
    ('田中', '花子', 'tanaka@example.com', 350000, 2, '1990-07-08'),
    ('伊藤', '雪子', 'ito@example.com', 400000, 1, '1991-09-10'),
    ('渡辺', '月子', 'watanabe@example.com', 400000, 2, '1992-11-12');

次に、init.sql ファイルを読み込んで実行します。

$ psql -f init.sql anondb
CREATE TABLE
CREATE TABLE
INSERT 0 2
INSERT 0 6
$ psql anondb
=# SELECT * FROM departments;
 id |  name
----+--------
  1 | 営業部
  2 | 技術部
(2 行)

=# SELECT * FROM employees;
 id | last_name | first_name |         email         | salary | department_id |   birth
----+-----------+------------+-----------------------+--------+---------------+------------
  1 | 佐藤      | 太郎       | sato@example.com      | 300000 |             1 | 1987-01-02
  2 | 鈴木      | 次郎       | suzuki@example.com    | 300000 |             2 | 1988-03-04
  3 | 高橋      | 三郎       | takahashi@example.com | 350000 |             1 | 1989-05-06
  4 | 田中      | 花子       | tanaka@example.com    | 350000 |             2 | 1990-07-08
  5 | 伊藤      | 雪子       | ito@example.com       | 400000 |             1 | 1991-09-10
  6 | 渡辺      | 月子       | watanabe@example.com  | 400000 |             2 | 1992-11-12
(6 行)

次に、マスキングルールを定義します。社員の E メールアドレスを格納した列 emailanon.partial_email 関数でマスキングすようにルールを定義します。

=# SECURITY LABEL FOR anon ON COLUMN employees.email
-#     IS 'MASKED WITH FUNCTION anon.partial_email(email)';
SECURITY LABEL

マスキングルールの定義には SECURITY LABEL コマンドを使います。SECURITY LABEL コマンドはセキュリティラベルを定義するコマンドです。私は SELinux 専用のコマンドだと勘違いしていましたが、ラベルプロバイダを指定することにより、任意のアクセス制御を適用できるそうです。FOR の後にはラベルプロバイダとして anonON COLUMN の後にはマスキング対象の列として employees.emailIS の後にはラベルとしてマスキングルールを指定します。

マスキングルールは anon.partial_email 関数を使うというものです。anon.partial_email 関数は PostgreSQL Anonymizer が提供するマスキング関数の 1 つで、E メールアドレスのローカル部、ドメインの最初の 2 文字と最後のサブドメインを残して ****** に置き換える関数です。例えば、E メールアドレス sato@example.comsa******@ex******.com にマスキングされます。

定義したマスキングルールはシステムビュー pg_seclabels で確認できます。また、マスキングルールを削除するには、ラベルに NULL を指定して SECURITY LABEL コマンドを実行します。

=# SELECT * FROM pg_seclabels;
 objoid | classoid | objsubid | objtype | objnamespace |     objname     | provider |                     label
--------+----------+----------+---------+--------------+-----------------+----------+------------------------------------------------
  16645 |     1259 |        4 | column  |         2200 | employees.email | anon     | MASKED WITH FUNCTION anon.partial_email(email)
(1 行)

インプレース匿名化

インプレース匿名化 (in-place anonymization) はデータをマスキングして更新する機能です。

インプレース匿名化を行うには anonymize_database または anonymize_tableanonymize_column 関数を使います。関数名を見れば分かると思いますが、anonymize_database 関数はデータベース内のすべてのテーブル、anonymize_table 関数は指定したテーブル、anonymize_column は指定したテーブルの列がマスキング対象になります。

=# BEGIN;
BEGIN
=# SELECT anon.anonymize_database();
 anonymize_database
--------------------
 t
(1 行)

=# SELECT * FROM employees;
 id | last_name | first_name |         email         | salary | department_id |   birth
----+-----------+------------+-----------------------+--------+---------------+------------
  1 | 佐藤      | 太郎       | sa******@ex******.com | 300000 |             1 | 1987-01-02
  2 | 鈴木      | 次郎       | su******@ex******.com | 300000 |             2 | 1988-03-04
  3 | 高橋      | 三郎       | ta******@ex******.com | 350000 |             1 | 1989-05-06
  4 | 田中      | 花子       | ta******@ex******.com | 350000 |             2 | 1990-07-08
  5 | 伊藤      | 雪子       | it******@ex******.com | 400000 |             1 | 1991-09-10
  6 | 渡辺      | 月子       | wa******@ex******.com | 400000 |             2 | 1992-11-12
(6 行)

=# ROLLBACK;
ROLLBACK
=# \q

なお、インプレース匿名化はデータ自体を更新するので、元のデータが失われてしまいます。また、テーブル内のすべてのデータを更新するので、実行に時間がかかります。後述の 匿名ダンプ でデータをマスキングしてダンプし、リストアしなおしたほうが速い場合があります。

匿名ダンプ

匿名ダンプ (anonymous dump) はデータをマスキングして SQL 形式でダンプする機能です。

匿名ダンプを行うには anon.dump 関数を実行します。ファイルにダンプする場合には、コマンドラインで関数を実行し、その出力をリダイレクトします。その際、余計なメッセージや位置揃え、行以外の出力を抑止するため、-qAt オプションを指定します。

$ psql -qAt -c "SELECT anon.dump()" anondb
(省略)
 COPY departments
                              FROM STDIN
                              CSV QUOTE AS '"'
                              DELIMITER ',';
1,営業部
2,技術部
\.

 COPY employees
                              FROM STDIN
                              CSV QUOTE AS '"'
                              DELIMITER ',';
1,佐藤,太郎,sa******@ex******.com,300000,1,1987-01-02
2,鈴木,次郎,su******@ex******.com,300000,2,1988-03-04
3,高橋,三郎,ta******@ex******.com,350000,1,1989-05-06
4,田中,花子,ta******@ex******.com,350000,2,1990-07-08
5,伊藤,雪子,it******@ex******.com,400000,1,1991-09-10
6,渡辺,月子,wa******@ex******.com,400000,2,1992-11-12
\.

動的マスキング

動的マスキング (dynamic masking) はデータを動的にマスキングし、ユーザにアクセスさせる機能です。元のテーブルと同じ名前のビューを別のスキーマにマスキング関数をかまして定義し、スキーマを切り換えてユーザにアクセスさせることで実現しています。

まず、動的マスキングを有効にするため、anon.start_dynamic_masking 関数を実行します。

=# SELECT anon.start_dynamic_masking();
 start_dynamic_masking
-----------------------
 t
(1 行)

次に、マスキングしたデータにアクセスさせるユーザ masked_user を作成します。

=# CREATE ROLE masked_user LOGIN;
CREATE ROLE
=# SECURITY LABEL FOR anon ON ROLE masked_user IS 'MASKED';
SECURITY LABEL
=# GRANT SELECT ON ALL TABLES IN SCHEMA mask TO masked_user;
GRANT
=# \q

CREATE ROLE コマンドで LOGIN 権限をもったユーザを作成し、SECURITY LABEL コマンドで MASKED ラベルを与えます。MASKED ラベルつきユーザのアクセスはマスキングしたテーブルが格納されたスキーマ mask に自動的に切り換えられます。アクセス権限は手動で設定する必要があります。GRANT コマンドで mask スキーマ内のすべてのテーブルに SELECT 権限を与えます。

最後に、masked_user ユーザとして anondb データベースに接続し、データがマスキングされていることを確認します。

$ psql -U masked_user anondb
=> SELECT * FROM employees;
 id | last_name | first_name |         email         | salary | department_id |   birth
----+-----------+------------+-----------------------+--------+---------------+------------
  1 | 佐藤      | 太郎       | sa******@ex******.com | 300000 |             1 | 1987-01-02
  2 | 鈴木      | 次郎       | su******@ex******.com | 300000 |             2 | 1988-03-04
  3 | 高橋      | 三郎       | ta******@ex******.com | 350000 |             1 | 1989-05-06
  4 | 田中      | 花子       | ta******@ex******.com | 350000 |             2 | 1990-07-08
  5 | 伊藤      | 雪子       | it******@ex******.com | 400000 |             1 | 1991-09-10
  6 | 渡辺      | 月子       | wa******@ex******.com | 400000 |             2 | 1992-11-12
(6 行)

=> \q

なお、動的マスキングはスキーマの切り換えで実現しているため、マスキング対象のスキーマは 1 つのみに限られます。デフォルトは public スキーマで、anon.start_dynamic_masking 関数の第 1 引数で指定できます。また、第 2 引数でマスキングしたテーブルを格納するスキーマも指定できます。


PostgreSQL Anonymizer の紹介はこれで完了です。今回は紹介しませんでしたが、マスキング関数はほかにも、ノイズ追加やシャッフル化、ランダム化、偽装、部分スクランブル化、一般化、k-匿名化など、いろいろな関数 があります。なお、人名や都市名などの偽装データは英語しか用意されていませんが、日本語の偽装データを CSV 形式で作成してロードすることもできます。

18
16
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
18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?