DataSunrise 動的マスキング機能を試してみた

# はじめに
 - この記事は以前導入した*Datasunrise*の動的マスキング機能を試すという内容です
 - 導入編は[こちら][1]

# マスキングの仕様
プリセットのマスキング機能は公式ドキュメントの```5.10.2 Masking Methods```に記載されています。
対象のカラムの型によって適用可能なプリセットフィルタが決まっているようです。(数字置換系ルールはINTやVARCHAR、文字列置換系のルールはVARCHARといった具合)
ちなみにTEXT型は```Default```か```Unstructured```、または```Function call```のみ適用可能らしい(困った)

- マスキングの仕組み

[DataSunrise_Database_Security_Suite_User_Guide][2]の```5.1 execution order of DataSunrise Rulues```より、
If the query was not blocked at the previous stage, it is examined for matching conditions defined by existing Masking Rules. If a Masking Rule matches, DataSunrise modifies the query's code according to the Rule's settings and redirects the modified query to the target DB. Having received the modified query, the target DB edits its response and outputs obfuscated ("masked") values instead of actual DB contents.

ルールが一致するとDataSunriseはSQLコードを変更し、それをターゲットDBにリダイレクトして難読化した結果を得る、みたいなところでしょうか。

# 試してみた
## 1.環境について
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570763/774300cf-cbe5-0d1d-08ff-0bb6dc601718.png)

構成イメージは上記の通りです。DBはAurora MySQLです。
今回はDataSunriseを導入したインスタンスにSSMでアクセスし、```mysql -h [DataSunriseインスタンスのhost名] -u [ユーザ名] -p -P [proxyのポート番号]```でDataSunrise経由でDBにアクセスします。その後Sunriseでプリセットされているマスキング機能を試していきます。

ちなみに、上記DBの接続を保ったままGUIでのマスキング設定を行った場合、DBに再接続することなくマスキングが適用されます。これはマスキングを試す際地味に便利な仕様です。

設定自体はDataSunriseのGUIから実施します。(CLIも可)

**※DB名/カラム名等はQiita記事作成の際に手動マスキングしています。**

## 2.1 Fixed String
対象文字列を任意の文字列で置き換えるというもの。
1つのルールごとに置き換え先の文字列はひとつしか入力できません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570763/2ce240ec-fe20-5ac5-53ee-02ec10981bc6.png)
黒塗りしていますが、マスキング対象のDB/テーブル/カラムを選択可能です。


 ```
MySQL [*****]> select ***** from ***** limit 10;
+--------------+
| *****         |
+--------------+
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
| 見本太郎     |
+--------------+
```
対象カラムをselectすると入力した文字列に変換されています。


## 2.2 Credit card masking
 - 入力文字列内の数字をXで置き換えるルール。
 - ハイフン区切りを認識し、最後の数字はマスキングしないようです。
    - 1234-5678-9012-3456ならXXXX-XXXX-XXXX-3456みたいな
 - 今回はこれを電話番号のマスキングに活用できるか試してみました。
 
 ```
MySQL [*****]> select distinct ***** from ***** limit 10;
+--------------+
| *****   |
+--------------+
| XX-XXXX-2555 |
| XX-XXXX-1234 |
| XX-XXXX-0241 |
| XX-XXXX-1235 |
| XX-XXXX-3838 |
| XXX-XXX-6610 |
| XX-XXXX-4871 |
| XXX-XXX-5750 |
| XXXX-XX-5368 |
| XX-XXXX-6216 |
+--------------+
10 rows in set (0.00 sec)
```
市外局番も含めよしなにマスキングしてくれています。

```
MySQL [*****]> select a.ct from (select ***** as test from *****) as a limit 10;
+--------------+
| test           |
+--------------+
| XX-XXXX-2555 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
| XX-XXXX-1234 |
+--------------+
10 rows in set (0.00 sec)
```
対象カラムをサブクエリ内で呼び、エイリアスをつけるパターンでもマスキングされています。
これはうれしいですね。

## 2.3 Regexp replace
正規表現を用いた文字列置き換えが可能のようです。
正規表現のパターンと何で置き換えたいを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570763/3b622853-69cc-b3f8-e992-5c0624b9fe37.png)
電話番号や金額系に活用できるでしょうか。

```
MySQL [*****]> select ***** from ***** limit 10;
ERROR 1305 (42000): FUNCTION mysql.regexp_replace does not exist
```
実行したところ怒られました。今回のTargetDBはAurora MySQLなのですが、そもそもREGEX_REPLACE関数はPostgreSQLの関数なのでした。
MySQLでもバージョン8.0.4でREGEX_REPLACE関数がサポートされるらしいのですが、Aurora MySQLでは・・・。

## 2.4 Mask first chars
入力文字列の頭からn文字目までを任意の文字で置換するルールです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570763/1abeb962-2a76-8985-47da-2c3ae51e0a66.png)
頭から6文字目までを0で置き換える設定にしてみました。

```
MySQL [*****]> select distinct ***** from ***** limit 10;
+------------------------------------------------------------------------------+
| *****                                                                        |
+------------------------------------------------------------------------------+
| NULL                                                                         |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
| 000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx= |
+------------------------------------------------------------------------------+
10 rows in set (0.07 sec)
```
0で置換できています。

## 2.5 Random value like current
数値の置き換えに関するルールで、入力値と同じ桁数でランダムな数値を返します。
ちなみに数値系のルールは単純な置換の```Fixed number```や最大最小値を定めてその間のランダムな値を返す```Random from interval```がありますが、今回は試していません。なお、当然対象のカラムはINT等である必要があります。

```
MySQL [*****]> select ***** from ***** limit 10;
+-------------------+
| *****             |
+-------------------+
|           4262299 |
|                 1 |
|           3039358 |
|                 8 |
|           1387122 |
|           4432493 |
|                 7 |
|           7708035 |
|                 4 |
|           7560250 |
+-------------------+
10 rows in set (0.01 sec)
```
1桁と7桁が混在するカラムで試したところ、ランダムな値に置き換わっているのが確認できました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570763/2a5f8451-5b2b-8205-73f1-d1cfdb5f1fb2.png)
ちなみに、数値系のカラムと文字列系のカラムを同時に選択すると、Masking Methodは基本的なもの(全て空白にするもの等)しか選べなくなります。

## 2.6 Email masking
メールアドレスに関するルールです。これは文字列の最初と最後以外を*で置き換えるというものですが、他にもトップレベルのドメインだけを残す```Email masking full```や@の前だけを置き換える```Mask username of Email```ルールが用意されています。

 - Email masking


```
MySQL [*****]> select distinct ***** from ***** limit 10;
+---------------------------+
| *****               |
+---------------------------+
|                           |
| n***********@******.**m   |
| m***@******.**m           |
| k************@******.**m  |
| o**********@******.**m    |
| o***********@******.**m   |
| o************@******.**m  |
| o*************@******.**m |
+---------------------------+
8 rows in set (0.01 sec)
```



 - Email masking full


```
MySQL [*****]> select distinct ***** from ***** limit 10;
+---------------------------+
| *****               |
+---------------------------+
|                           |
| ************@******.com   |
| ****@******.com           |
| *************@******.com  |
| ***********@******.com    |
| **************@******.com |
+---------------------------+
6 rows in set (0.01 sec)
```

 - Mask username of Email

```
MySQL [*****]> select distinct ***** from ***** limit 10;
+---------------------------+
| *****               |
+---------------------------+
|                           |
| ************@XXXXX.com   |
| ****@XXXXX.com           |
| *************@XXXXX.com  |
| ***********@XXXXX.com    |
| **************@XXXXX.com |
+---------------------------+
```
※プロバイダ名は記事化に伴い"XXXXX"に手動マスキングしていますが、実際には表示されます。



ちなみにカウントもよしなにやってくれます。

```
MySQL [*****]> select count(*****) from ***** limit 10;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| count(case when LENGTH(trim(*****)) <= 1 then ***** else CONCAT( LEFT(trim(*****), 1) , INSERT(INSERT( REPEAT('*', LENGTH(trim(*****))-2), INSTR(trim(*****), '@') - 1, 1, '@' ), INSTR(trim(*****), '.')-1, 1, '.' ) , RIGH |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                           3084 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 3077 warnings (0.01 sec)
```


 - 直接DBに繋いで試した場合


```
MySQL [*****]> select count(*****) from ***** limit 10;
+--------------------+
| count(*****) |
+--------------------+
|               3084 |
+--------------------+
1 row in set (0.01 sec)
```

カウント数が一致しているのが確認できます。


## 2.7 おまけ TEXT型について
2.1-2.6で試したルールはVARCHAR型やINT型等のカラムでしたが、当社にてマスキングしたいカラムにTEXT型のものがあります。しかしながら、TEXT型カラムを選んだ場合、選択できるMasking Methodは```Default```か```Function call```、```unstructured masking```ルールのみです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570763/4546c4aa-f1ce-b374-98d4-6f4f82bc9553.png)
TEXT型はEmpty Spaceで置き換えるか事前に仕込んだFUNCTIONを呼び出すしか選択肢がないことを表しています。
```unstructured masking```は```Replacing potentially sensitive values with the "*" character```とのことですが、試したところ日本語には対応していませんでした。(何もマスキングされませんでした)

 - Defaultを試す

```
MySQL [*****]> select ***** from ***** limit 10;
+----------+
| ***** |
+----------+
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
|          |
+----------+
10 rows in set (0.00 sec)
```
 - unstructured maskingを試す


```
ySQL [*****]> select ***** from ***** limit 1;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| contents                                                                                                                     |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| XXXXXXXXXXXXXXXXXXXXXXXXXXX
```
※記事化に伴い手動でXで置換しましたが、マスキングされていませんでした。



# まとめ
簡単な設定だけでこれだけのマスキングができるというのは、かなりありがたいのではないかと思います。
マスキング以外の機能も試してみたいところですね。

# Reference
https://www.datasunrise.com/ 

[1]:https://qiita.com/t_odash/items/5792cd2aaf6adda6b4a2
[2]:https://www.datasunrise.com/documents/DataSunrise_Database_Security_Suite_User_Guide.pdf