BigQueryからの移行を見据えたClickHouseでのDynamic Masking実装ガイド

BigQueryからClickHouseへのマイグレーションを検討する場合、セキュリティやデータガバナンスの観点は非常に重要です。特に、個人情報や機密情報を取り扱う際に求められる「Dynamic Masking（動的マスキング）」は欠かせない機能のひとつです。本記事では、BigQueryで実現していたDynamic Maskingに近い仕組みを、ClickHouseでどのように構築できるかを紹介します。

---

## 1. Dynamic Maskingの重要性とシナリオ

### 1.1 企業でのコンプライアンス要件
近年、個人情報保護や金融商品取引法など、さまざまなコンプライアンスが要求されるようになりました。特定のユーザには機密情報を一部伏せた状態でしか参照させない、という要件を満たすために、DB上でのマスキング機能が求められています。

### 1.2 システム開発・運用現場での利用シナリオ
- **開発・テスト環境**  
  実際のデータを参照したいが個人情報は伏せたい場合、部分的にマスキングされたデータだけを開発者へ共有します。
- **外部ベンダーとのやり取り**  
  特定の外部ベンダーがシステム監査を行うとき、個人情報や財務情報など重要データを完全に渡すことは避けたいが、動作検証は行いたいというケースに適しています。
- **部署や役職によるアクセス制御**  
  役職や担当業務に応じて、表示するカラムや参照できる行を制御するシステム要件にも応用できます。

### 1.3 BigQueryとの比較
BigQueryではDynamic Maskingが標準機能として提供されており、ポリシータグなどを用いて細やかな列・行制御が可能です。一方でClickHouseでは、公式に「Dynamic Masking」という名前の機能はありません。しかし、**View** と **ユーザ権限** を組み合わせることで、ほぼ同等の制御が可能となります。

---

## 2. ClickHouseでのDynamic Masking実現方法

ここからは、実際の構築手順を解説します。

### 2.1 テーブル作成とサンプルデータ投入

まずは、以下のようなテーブルを作成し、サンプルデータを用意します。

```sql
-- テーブルを作成
CREATE TABLE table1
(
    id     UInt64,
    name   String,
    city   String,
    salary UInt64
)
ENGINE = MergeTree()
ORDER BY id;

-- サンプルデータを投入
INSERT INTO table1 (id, name, city, salary) VALUES
    (1, 'Alice',   'Tokyo',  100),
    (2, 'Bob',     'Osaka',  200),
    (3, 'Charlie', 'Tokyo',  300),
    (4, 'Dave',    'Nagoya', 400),
    (5, 'Eve',     'Osaka',  500);

-- データの確認
SELECT * FROM table1;
```

### 2.2 カラムを制限するViewの作成

ユーザに見せたいカラムだけを抽出したViewを作成します。ここでは `salary` を除外し、`id`, `name`, `city` のみを参照させるViewを作っています。

```sql
CREATE VIEW table1_column_view SQL SECURITY DEFINER AS
SELECT
    id,
    name,
    city
FROM table1;

-- 動作確認
SELECT * FROM table1_column_view;
```

### 2.3 行を制限するViewの作成

行レベルでアクセス制限をかけたいケースには、条件付きで行を絞り込むViewが有効です。たとえば `city = 'Tokyo'` の行だけが参照できるViewを用意します。

```sql
CREATE VIEW table1_row_view SQL SECURITY DEFINER AS
SELECT
    *
FROM table1
WHERE city = 'Tokyo';

-- 動作確認
SELECT * FROM table1_row_view;
```

### 2.4 一部のデータをマスキングするViewの作成

さらに、特定条件下でのみ正しい値を返却し、それ以外はマスキング（ここでは0）を返すViewを作成します。下記例では、`city = 'Tokyo'` の場合のみ本来の `salary` を表示し、それ以外の `city` の `salary` を0に置き換えます。

```sql
CREATE VIEW table1_mask_view SQL SECURITY DEFINER AS
SELECT
    id,
    name,
    city,
    CASE
      WHEN city = 'Tokyo' THEN salary
      ELSE 0
    END AS salary
FROM table1;

-- 動作確認
SELECT * FROM table1_mask_view;
```

---

## 3. ユーザ・ロール作成と権限付与

BigQuery同様に、特定のロールやユーザへだけViewの参照権限を与えることで、元テーブルへの直接アクセスを制限できます。

```sql
-- ユーザとロールの作成
CREATE USER test_user IDENTIFIED WITH SHA256_PASSWORD BY 'xxxxxxxx';
CREATE ROLE test_role;
GRANT test_role TO test_user;

-- 作成したViewへの権限付与
GRANT SELECT ON table1_column_view TO test_role;
GRANT SELECT ON table1_row_view TO test_role;
GRANT SELECT ON table1_mask_view TO test_role;
```

---

## 4. アクセス検証

以下の例では、作業者が`test_user`に切り替えた後の挙動を確認しています。

### 4.1 元テーブルへのアクセス

```sql
SELECT *
FROM table1
```

実行すると、権限不足のため以下のエラーが返されます。

```
Received exception from server (version 24.8.1):
Code: 497. DB::Exception: Received from gbqp9vxmtt.ap-southeast-1.aws.clickhouse.cloud:9440.
DB::Exception: test_user: Not enough privileges.
To execute this query, it's necessary to have the grant
SELECT(id, name, city, salary) ON default.table1. (ACCESS_DENIED)
```

### 4.2 カラムを制限したViewの参照

```sql
SELECT *
FROM table1_column_view;
```

結果は以下の通りで、カラムは `id`, `name`, `city` のみが取得可能です。

```
   ┌─id─┬─name────┬─city───┐
1. │  1 │ Alice   │ Tokyo  │
2. │  2 │ Bob     │ Osaka  │
3. │  3 │ Charlie │ Tokyo  │
4. │  4 │ Dave    │ Nagoya │
5. │  5 │ Eve     │ Osaka  │
   └────┴─────────┴────────┘
```

### 4.3 行を制限したViewの参照

```sql
SELECT *
FROM table1_row_view;
```

`city = 'Tokyo'` の行のみ参照できます。

```
   ┌─id─┬─name────┬─city──┬─salary─┐
1. │  1 │ Alice   │ Tokyo │    100 │
2. │  3 │ Charlie │ Tokyo │    300 │
   └────┴─────────┴───────┴────────┘
```

### 4.4 一部データをマスキングしたViewの参照

```sql
SELECT *
FROM table1_mask_view;
```

`city = 'Tokyo'` の場合のみ本来の `salary` が表示され、それ以外は0に置き換えられます。

```
   ┌─id─┬─name────┬─city───┬─salary─┐
1. │  1 │ Alice   │ Tokyo  │    100 │
2. │  2 │ Bob     │ Osaka  │      0 │
3. │  3 │ Charlie │ Tokyo  │    300 │
4. │  4 │ Dave    │ Nagoya │      0 │
5. │  5 │ Eve     │ Osaka  │      0 │
   └────┴─────────┴────────┴────────┘
```

---

## 5. まとめ

BigQueryのDynamic Maskingを活用していた企業やチームにとって、ClickHouseへの移行時にデータの秘匿化機能が必要となるケースは多いでしょう。ClickHouseは標準でDynamic Maskingの名前こそありませんが、**View** を活用し、さらにユーザやロールによる権限付与を行うことで、**列単位・行単位・値のマスキング**を自由に組み合わせたアクセス制御が実現できます。

- どのカラムを見せるのか  
- どの行を見せるのか  
- 見せるときに一部データを変換（マスキング）するのか  

こうした制御を組み合わせれば、BigQuery同様の複雑な要件にも柔軟に対応可能です。マイグレーションの際には、必要な権限ポリシーを改めて洗い出し、ClickHouse上でもViewやユーザロールを適切に設定することで、セキュリティと利便性を両立した仕組みが構築できるでしょう。