こんにちは!那須どうぶつ王国でスナネコの赤ちゃんの一般公開が開始された1ことに喜びを感じている、SRE部エンジニアの塩崎です。
ZOZOTOWNでメルマガやPUSHの配信のために、IIAS(IBM Integrated Analytics System)というDWHアプライアンスを利用しています。このIIASは以前から利用していたPureDataの後続機で、先日にマイグレーションをしたので、よろしかったらそちらの記事をお読みください。
概要
メルマガを配信するにあたって、メールアドレスや氏名などの個人情報を扱う必要があります。これらの情報は特に取扱いに注意を要するものであり、情報を参照できるユーザーを可能な限り限定する必要があります。素朴な解決策として、個人情報が入っているテーブル全体に対するSELECT権限をREVOKEすることが思いつきます。
しかし、この方法を採用するとテーブル全体へのアクセス権が失われるため、運用作業に影響の出る恐れがあります。特に非正規化したデータマートのテーブルは横長になりがちなので、その傾向が強くなります。
そこで、我々のチームではIIASのRCAC(Row and Column Access Control)機能を使い、隠す必要のある「列」だけをピンポイントでマスクしました。
本記事ではこのRCAC機能の設定方法やRCAC機能のアドバンストな使い方、他のDBでも使える同等の機能の紹介などをいたします。
参考: Row and column access control (RCAC) overview
個人情報をマスクする素朴な実装とその問題点
最初に個人情報などをマスクする場合の素朴な実装と、それを採った際に発生しうる問題点を説明します。
テーブルに対するSELECT権をREVOKEする
権限を剥奪するならREVOKEだということで、REVOKE文を実行することでSELECT権を剥奪することが真っ先に思い浮かぶ方は多いかと思います。
この場合は、以下のようなSQL文を実行することになります。
REVOKE SELECT ON <テーブル名> TO <ユーザー名・ロール名>;
ここで注意が必要なのは、REVOKE文で指定することができる最小粒度はテーブルであるということです。権限を設定したい境界線の全てがちょうどテーブルとテーブルの間にあるならば何も問題はないです。
ですが、多くのケースではそうなっていることの方が稀です。特に歴史のあるサービスで、テーブルに対するカラム追加が何回も行われていることは珍しくありません。そのようなケースでは権限境界がテーブルの中にまで入り込むことが多々あります。REVOKE文を使った権限管理では不必要に厳しいルールになりがちで、現場での運用負荷の上がる恐れがあります。
個人情報が含まれないVIEWを作成する
次に思いつく実装は個人情報が含まれていないVIEWを作成し、元のテーブルの参照権限をREVOKEすることです。
name列とemail列に個人情報が入っているusersテーブルがある場合、以下のVIEWを作ることでこれらの列をマスクできます。
CREATE VIEW users_without_personal_information AS ( SELECT id, NULL as name, NULL as email, created_at, updated_at FROM users ); REVOKE SELECT ON users TO <ユーザー名・ロール名>; GRANT SELECT ON users_without_personal_information TO <ユーザー名・ロール名>;
この方法であれば列単位での細やかな権限設定が可能です。
しかし、TABLEとVIEWの二重管理が発生し、テーブル追加やカラム追加の時の手間が倍増します。
IIASで使える冴えたやり方
列単位で個人情報をマスクしたいけど、オブジェクトの数を増やしたくはないという課題を解決するために、IIASのRCAC機能を利用しました。
以降でこの機能の使い方を説明していきます。
サンプルテーブルの作成
まずは、サンプルテーブルの作成とデータの投入を行います。
CREATE TABLE users ( id INTEGER, email VARCHAR(255) ); INSERT INTO users VALUES (1, 'sato@example.com'), (2, 'suzuki@example.com'), (3, 'takahashi@example.com'), (4, 'tanaka@example.com');
個人情報の閲覧用ロールの作成
次に、個人情報の閲覧用ロールを作成します。このロールそのものには何も権限をGRANTせずに運用します。
CREATE ROLE personal_information_viewer;
個人情報マスクの作成・適用
ここまでで必要なテーブル・ロールの準備は済んだので、いよいよ本題に入ります。
個人情報を保護するためのマスクを作成し、その有効化を行います。3行目のCASE式でクエリを投げたユーザーが個人情報の閲覧用ロールに属しているか否かを判定し、その結果によってemailをNULLにするかどうかを決めています。
CREATE MASK EMAIL_MASK ON users FOR COLUMN email RETURN CASE WHEN VERIFY_ROLE_FOR_USER(SESSION_USER,'PERSONAL_INFORMATION_VIEWER') = 1 THEN email ELSE NULL END ENABLE; ALTER TABLE users ACTIVATE COLUMN ACCESS CONTROL;
個人情報を見るための権限をユーザーに付与
このままですと、全ユーザーがemailを読み出すことができないので、個人情報の閲覧権限をユーザーに渡しましょう。
GRANT ROLE PERSONAL_INFORMATION_VIEWER TO USER <ユーザー名>;
また、権限を剥奪するためには、以下のSQLを実行します。
REVOKE ROLE PERSONAL_INFORMATION_VIEWER TO USER <ユーザー名>;
個人情報の閲覧権限の有無によるSELECT結果の変化
最後に、権限の有無によってSELECT結果が変わることを確認します。
権限を持っているユーザーが SELECT * FROM users
を実行した時の結果を以下に示します。マスク対象のメールアドレスが正常に閲覧できていることが分かります。なお、個人情報の閲覧権限とは別にuserテーブルそのものに対するSELECT権限も必要です。
一方で権限を持っていないユーザーが同様のクエリを実行すると以下の結果になります。メールアドレスがマスクされ、NULLになっていることが確認できます。
応用編
ここからは、この機能の応用編です。アドバンストな使い方や、他のDBで同様の機能の紹介をします。
データをNULLに置換するのではなく、部分的に見せたい
上記の例ではデータをNULLに置換していましたが、一部分だけをマスクしたいというケースはよくあります。ここではその一例として、メールアドレスのローカル部のみをマスクし、ドメインはマスクせずに表示することを考えます。
例えば、 sato@example.com
というメールアドレスは @example.com
に置換されます。
CREATE MASK文のRETURN句の後ろには任意のCASE式を書くことができます。そのため、以下のMASKを作成することでドメインのみを返すことができます。
CREATE MASK EMAIL_MASK ON users FOR COLUMN email RETURN CASE WHEN VERIFY_ROLE_FOR_USER(SESSION_USER,'PERSONAL_INFORMATION_VIEWER') = 1 THEN email ELSE SUBSTRING(email, INSTR(email, '@')) END ENABLE;
この方法は他にも応用でき、例えば電話番号の下4桁のみを返したり、SHA-2関数などでハッシュ化した情報を返したりが考えられます。
IIAS以外のDBでも使いたい
このような柔軟なマスク機能は便利なので他のDBでも使いたいです。IIAS以外のDBでは同等の機能があるかどうかを調査しました。
MySQL
現時点ではMySQLで同等の機能はありません。ドキュメントを読み足りないだけかもしれないので、もし同様の機能がある場合は教えて頂けますと幸いです。
MySQL Enterprise Edition 5.7以降でData Maskingがありますが、求めているものとは少し違います。個人情報の一部をXXXに置き換えるための便利な関数が提供されたり、ダミーデータ生成用の便利な関数が提供されるだけなようです。
PostgreSQL
PostgreSQLも同様の機能はありません。ドキュメントを読み足りないだけかもしれな(略)。
PostgreSQL Anonymizerという拡張機能がありますが、これもMySQLのData Masking機能と同様で、値の一部を置き換えるための便利関数を提供しているのみです。
SQL Server
SQL ServerのDynamic Data Masking機能は今回紹介したIIASの機能とほぼ同等の柔軟性を持っています。個人情報マスク済みのVIEWを作らずに個人情報マスクを実現でき、またマスクする際には一部分のみマスクすることも可能です。ただし、バージョン2016以降でないと使えない点に注意が必要です。
Google BigQuery
Google BigQueryでは、2020年6月18日現在でcolumn-level security機能がベータ版として提供されています。この機能を使うことによって、特定の列をマスクできます。ただし、データの一部のみをマスクすることはできず、特定の列を完全に見せるか見せないかのどちらかしか実現できません。
まとめ
IIASのRCAC機能を使うことで個人情報などのセンシティブなデータをマスクする仕組みを作ることに成功しました。この仕組みを使い、より安全に日々の運用業務を行うことができるようになることが期待されます。
ZOZOテクノロジーズではセキュリティと利用者の利便性の両立を目指すことができる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください!