0
1

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 1 year has passed since last update.

Amazon Redshift で ダイナミックデータマスキング

Last updated at Posted at 2022-09-23

顧客機微データ(PIIやPCI)をRedshift内で扱う場合の対処法には色々対策が想定される

例)

  • クラスタの暗号化(AWS KMS)
  • 行レベルセキュリティや列レベルセキュリティで対象データへのアクセス制御
  • 対象データをマスクしたビューを作成
  • 対象データをハッシュ化
  • 対象データをトークン化
  • ダイナミックデータマスキング
  • など

ここでは、ダイナミックデータマスキングを試してみる

ダイナミックデータマスキングとは?

権限のないユーザに対してのみ、機密データをマスクした状態でしか見せないようにすること。
具体的には、ポリシーに従って電話番号やメールアドレスなど個人情報を***とマスクして表示させるなどのイメージ。
そもそも見せたくない列や行ごと固定的に非表示にする方法(列レベル/行レベルセキュリティ)でも良いユースケースが多そうだが、生データは見せたくないけどJOINや集計には使いたい場合など、生ではない状態でも一意性を識別したい場合などが想定される。

どうやる?

以下はその2の方法

実現方法

その2のGitHubのaws-samplesで紹介されている方法を試してみる
https://github.com/aws-samples/amazon-redshift-dynamic-data-masking#masking-privileges

ざっくりした仕組み

  • マスキング処理はPython UDF(f_mask_varchar)を利用
  • 生データはtableで持ち、ユーザによってマスクされた情報を動的にViewで出し分ける
  • 誰にどのような方法で見せるかのポリシーは、管理テーブル(user_entitle)で設定

細かい要件

以下の要件を満たす実装としている。

  • マスキング ルールは、タイプ (電子メール、SSN、汎用、生年月日など) に基づいて列ごとに異なる場合がある。各列にタイプのタグを付けて、使用するルールを適応できる。
  • マスキング権限はユーザー レベルで割り当てることができ、ユーザーがアクセスできるすべての DB オブジェクトに適用できる。ただし、DB オブジェクトへのアクセスは引き続き DB ユーザー/グループ レベルで制御される。フィールドが PII としてタグ付けされている場合、次の権限が含まれる。
    • FullMask – データは難読化されて返されますが、元の値を特定することはできない。
    • PartialMask – 入力値の一部はマスクされますが、一部はマスクされない。
    • NoMasking – 入力値が返される。
    • 未定義 - ユーザーには上記の権限が割り当てられておらず、NULL が返される。
  • クエリのパフォーマンスは、元のパフォーマンスにできるだけ近づける。
  • アプリケーションへの影響は最小限に抑える。
  • コンプライアンス、監査、および管理が実施されており、監査人は次の人物を決定できる。
    • 修正されたマスキング ルール
    • 変更されたマスキング権限
    • マスクされたデータ

やってみた 〜セットアップ

1) RedshiftにUDF関数作成

あらかじめRedshift Serverless(ProvisionedでもOK)を準備。
AWS Cloud9などでGitHubからクローンする
git clone https://github.com/aws-samples/amazon-redshift-dynamic-data-masking.git

UDF作成用のpythonコードがあるので実行する

pip3 install redshift_connector
python3 create_udfs.py
# 以下を入力
Cluster Host:
Database Name:
User:
Port:
Password:

2) サンプル生データ登録

Query Editor v2などを使って、Redshiftにクエリ実行。
サンプル生データテーブル作成

create table public.customer_raw(id int, first_name varchar(100), last_name varchar(100), login varchar(100), email_address varchar(100));
insert into public.customer_raw values
 (1,'Jane','Doe','jdoe','jdoe@org.com'),
 (2,'John','Doe','jhndoe','jhndoe@org.com'),
 (3,'Edward','Jones','ejones','ejones@org.com'),
 (4,'Mary','Contrary','mcontrary','mcontrary@org.com');

3) データマスク用ビュー作成

生データをそのまま見せるのではなく、権限に応じてUDFでマスク処理を動的に適応したビューを生成する

create or replace view public.customer as (
  select c.id,
    f_mask_varchar(c.first_name, 'name', e.priv) first_name,
    f_mask_varchar(c.last_name, 'name', e.priv) last_name,
    f_mask_varchar(c.login, 'login', e.priv) login,
    f_mask_varchar(c.email_address,'email', e.priv) email
  from  public.customer_raw c
  left join public.user_entitle e on (current_user = e.username)
) with no schema binding;

生データのテーブルと、マスキングポリシーの権限管理テーブル(user_entitle)とのJOINで、かつuser_entitleテーブルの権限設定(priv)に応じて、各データの表示形式をUDFで切り替える

4) ビューから呼ばれるUDFを定義

create or replace function f_mask_varchar (varchar, varchar, varchar)
  returns varchar
immutable
as $$
  select case
    when $3 is null then null
    when $3 = 'N' then $1
    when $3 = 'F' then md5($1)
    else case $2
      when 'ssn' then substring($1, 1, 7)||'xxxx'
      when 'email' then substring(SPLIT_PART($1, '@', 1), 1, 3) + 'xxxx@' + SPLIT_PART($1, '@', 2)
      else substring($1, 1, 3)||'xxxxx' end
    end
$$ language sql;

処理内容)

  • 未登録ユーザの場合は NULL
  • N (NoMasking):マスクしない(そのまま)
  • F(FullMask):md5でハッシュ化 
  • nullの場合;NULLを返却
  • その他:PartialMaskの扱いで部分的にマスク処理

ここは要件に応じて好きに変えれば良い。(例えばmd5をSHA1やその他に変えるなど)

5) 権限管理テーブル作成

create table public.user_entitle (username varchar(25), priv varchar(1));

テスト用に複数の権限のユーザを作成して登録

create user u_fullmask password disable;
create user u_partialmask password disable;
create user u_nomask password disable;
create user u_newuser password disable;

grant select on customer to u_fullmask;
grant select on customer to u_partialmask;
grant select on customer to u_nomask;
grant select on customer to u_newuser;

grant select on user_entitle to u_fullmask;
grant select on user_entitle to u_partialmask;
grant select on user_entitle to u_nomask;
grant select on user_entitle to u_newuser;

insert into public.user_entitle values
 ('u_fullmask', 'F'),
 ('u_partialmask', 'P'),
 ('u_nomask', 'N');
  • u_fullmaskユーザには全マスク
  • u_partialmaskユーザには一部マスク
  • u_nomaskユーザにはマスクしない

動作確認

全マスクユーザの場合

SET SESSION AUTHORIZATION 'u_fullmask';
select * from customer;

image.png

全部ハッシュ化される

一部マスク対象ユーザの場合

image.png

一部がxxxxに
※どこまでどうマスクするかは、f_mask_varcharで自由に変えればよい

全表示対象ユーザの場合

image.png

マスクされない

権限管理テーブル(user_entitle)未登録ユーザの場合

image.png

NULLで表示

まとめ

UDFとビューを使って、どのユーザに、どのカラムを、どのような表示方式(マスク)かを動的に切り替えて表示させるダイナミックデータマスキングを割と簡単に実装できた。
特に自動的に氏名やメールアドレスを識別するわけではないが、自動検知は検知漏れのリスクも考慮すると、手堅く変換ルールを規定した方が安心できそう。
※ちなみに自動検知も正規表現を組み合わせばある程度実装できそうなのと、Glue/Glue DataBrewにはPII検知機能があるので、それらを使って前処理で対応する方法も考えられる。

要件次第では、列レベル/行レベルのアクセス制御や、単純な一律ハッシュ化、必要カラムのみ表示するビューを作るだけでも良さそうな気がするが、ダイナミックデータマスキングを実装したい場合はこんな実装もできることがわかる。

ちなみに、以下のblogでは、リッチに3rdパーティツールを活用した例も紹介されている
https://aws.amazon.com/jp/blogs/news/protect-and-audit-pii-data-in-amazon-redshift-with-datasunrise-security/

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?