[レポート] Securing data at scale with dbt & Snowflake(dbtとSnowflakeでデータをセキュアに管理する) #dbtcoalesce

セキュアなデータだけ別スキーマにわざわざ複製している組織の方は必見
2020.12.22

大阪オフィスの玉井です。

12月7日〜11日の間、Fishtown Analytics社がcoalesceというオンラインイベントを開催していました(SQLを触っている方はピンとくるイベント名ではないでしょうか)。

「Fishtown Analytics社って何やってる会社?」という感じですが、dbtというツールを開発しているベンダーです。dbtについては、下記をご覧ください。

今回は、その中からSecuring data at scale with dbt & Snowflakeというセッションを受講したので、レポートを記します。

イベント概要

公式

概要

You probably have customer data in your data warehouse — it's a must-have for understanding a business. However, this data almost definitely includes personally identifiable information (PII), which shouldn't be shared with the entire organization. In this session, we'll learn how JetBlue approaches the problem of masking PII at scale by leveraging some Snowflake features straight from their dbt project.

私なりの要約

データ分析で取り扱うデータの中には、往々にして個人情報が含まれています。こういったデータは、普通のデータと同じように管理するわけにはいきません(特に欧米は厳しい)。Jetblue社の、dbtとSnowflakeで、セキュアに取り扱う事例が紹介されました。

セッションレポート

自己紹介

ファーストチャレンジ:プライベートスキーマを用意してみた

  • 航空会社がDWH上でデータの安全性を確保することに注意を払うべき理由
  • 例:Jetblue社で航空券を予約する時
    • オンラインフォームで入力した情報のほとんどはJetblue社に送られる
    • そのデータはSnowflakeにロードされる
    • Snowflakeのデータをdbtを使って変換している
    • 当然、これらの一連のデータの流れはセキュリティ保護されている

  • フォームで入力する情報を考えてみる
    • 名字
    • 名前
    • 生年月日
    • 緊急連絡先
    • 入力者のIPアドレス
    • …などなど
  • Jetblue社に限らず、大体の航空会社はこれらのデータを受け取って管理している
    • これらのデータを安全に保護するのは航空会社の義務である

  • 個人情報を適切に取り扱わなかったことで金銭的なペナルティを受けた航空会社もいる
    • GDPR違反で多額の罰金を科された企業もいる
  • 特に現在、そもそも不景気(おそらくコロナ禍含む)であるため、お金もそんなにない
    • そもそも罰金を支払えるほど資金に余裕がない
    • 故に個人情報の保護は絶対に避けてはならない課題である

  • オンプレのDWHをクラウド(Snowflake)に移行するプロジェクトを始めた
  • 加えて、dbtを使用して、乗客と乗務員のデータを安全に保つ方法が必要
    • Jetblue社には、機密情報へのアクセスが必要な部署が存在する

  • 最初の試みとして、DWH内に「プライベートスキーマ」というものを作成した
    • 機密情報を含むデータモデルのコピーを作成する
    • そのモデルを別々のスキーマで具現化する
    • 片方のスキーマ名には_privateをつける
    • _privateがついているスキーマでは、機密情報を完全に公開する
  • 例:スライド右下
    • analytics.booking_private.passengersというテーブル
    • このテーブルでは、乗客の姓名は公開している
    • analytics.booking.passengersというテーブルを別途用意する
    • このテーブルの姓名はハッシュ化されている
  • 少数のデータアナリストだけ、_privateスキーマへのアクセスが許可されている
    • ユーザーの権限管理が必要

  • 「プライベートスキーマ」方式の別の側面
    • プライベートではない方のスキーマを構築する手順
    • 機密情報の暗号化の方式
  • Jetblue社が(この方式で)やったこと
    • 生のデータベースにテーブルを作成し、アカウント管理者だけがアクセスできるようにする
    • そのテーブルにはランダムな文字列が含まれている
    • そのテーブルをJOINに使用することで、どのデータモデルでも機密情報にアクセスできるようにした
    • ソルトとカラムの値を組み合わせてMD5ハッシュを適用する
    • ソルトがわからない限り、誰も解読できなくなる

  • 「プライベートスキーマ」方式を適用した結果…
    • このアプローチは功を奏した
    • Snowflakeのロールベースのアクセス制御でデータを保護できた
    • データアナリストと、より多くのデータを共有することができた
  • いくつかの欠点もあった
    • 新しくできたスキーマへクエリできるように権限を再設定する必要があった
    • 機密情報を含む各データモデルを複製する必要があった(dbtのコード量が35%増えた)
    • コードが増えたので、dbtモデルのコンパイルも時間がかかるようになった
  • 正直いって、この方法はベストではなかった

  • テーブルのバージョンが1つだけで済んだら楽になるのでは…と考えた
    • 機密情報を公開・非公開するときに、スキーマやモデルを2つずつ持つのをやめたい

セカンドチャレンジ:Snowflakeのダイナミックデータマスキングを使ってみた

  • 「プライベートスキーマ方式」を辞めて、ダイナミックデータマスキングを使う方法に移行することにした
    • マスキングポリシーを定義できる
    • このポリシーに合致しないユーザー(ロール)はデータを閲覧できない
  • 例:スライド右側
    • テーブルは1つだけ
    • ロールに応じて、出力結果が異なるようにした
  • この方法のメリット
    • データアナリストはクエリを細かくチェックする必要がなくなった
    • dbtプロジェクトを大幅にスリム化できた
    • コンパイル時間も短くなった

  • マスキングポリシーは、スキーマレベルで存在するオブジェクトである
    • テーブルやビューと同じ感じ
    • 同一スキーマ内のテーブルやビューの任意のカラムに適用できる
  • スライドに載せているSQLがマスキングポリシーを設定するサンプル
    • クエリを実行したロールがANALYSTではない場合、このポリシーが適用されたカラムの値はアスタリスクで埋められて表示される

  • この方法をスケールさせる必要がある
    • dbtモデルに組み込む必要がある
    • プロジェクト内に存在する全てのdbtモデルに展開する

  • データマスキングを行うdbtマクロを作成した
    • 引数に「マスキングしたいカラムのリスト」を渡す
  • スライドに例を載せている
    • 乗客の姓名と緊急連絡先のデータをもつモデルのconfig
    • これらのデータは間違いなく機密情報である
    • だからマクロを使ってマスキング

マクロの説明

(先程紹介したマクロがどう動くのかをもう少し詳しく説明するパート)

  • dbtモデルを作られる時…
    • データマスキングマクロを適用するためpost_hookを呼び出される
    • post_hookは、dbtモデルが作成された後に適用されるクエリを定義できる
    • pre-hook & post-hook | docs.getdbt.com
    • post_hookにデータマスキングマクロが使われている

  • マクロは最初にpost_hookを呼び出したモデルのコンテキストを読み込む
    • materializationのタイプ(テーブルかビューか等)
    • DB名
    • スキーマ名
    • エイリアス
    • …など
  • これらの値は後で役に立つ

  • 取得した値をマスキングポリシーを定義するマクロに代入する
    • スキーマ名など
  • TRANSFORMER_〜という名称のロールのみに閲覧権限を与える設定
  • 上記ロール以外はマスキングされた値しか閲覧できなくなる

  • STEP2で定義されたマスキングポリシーを、任意のカラムに適用する
    • テーブルだったらALTER TABLEが走る
    • ビューだったらビューのクエリを変更する
  • マクロの引数として渡している値(ポリシーを適用したいカラム)がここで使用される

  • マクロが適用された後のテーブル
    • スライド下部がTRANSFORMER_〜ロールでクエリした時の結果
    • スライド上部が上記ロール以外のユーザーがクエリした時の結果
  • 日付項目については、一定の未来の日付になっている
    • 実際の日付値の何の関連もない日付(つまり値からの予測は不可)

  • 最後の課題が残っている
    • ユーザーグループに、このマスキングポリシーへのアクセスを許可すること

マスキングポリシー自体へのアクセス

  • Jetblue社には、顧客名を見る必要があるチームがいる
    • 今の時点だと、TRANSFORMER_〜ロールは見れる
    • カスタマーサポートチームは見れない
    • 財務部も見れない
    • これらのチームにマスキングポリシーを許可する必要がある

  • 別のマクロを作成した
    • このマクロは、マスキングポリシーを持つSENSITIVEロールを作成する
    • 作成されるロールはスキーマ毎に分かれるようにした
    • 業務上必要なチームは(そのチームをユーザーグループとして定義しておき)ユーザーグループに対して、このマクロで新しいロールを作成して適用する

まとめと今後について

まとめ

  • Snowflakeのセキュリティコントロールが実現できた
    • 要するに、より多くの人に、より多くのデータを、安全に提供できることができるようになった
    • 本当に必要な人にだけ、機密情報へのアクセスが許可できるようになった
  • dbtマクロのおかげで、コードがシンプルになった
  • 監査等で、誰がどのデータにアクセスできるようになってるか報告するのも余裕になった
    • 簡単にピンポイントで特定できるようになった
    • 監査関係のレポートを作成するのもすごく簡単になった
  • 限られたグループのメンバーだけが機密情報にアクセスできることを把握できているため、精神衛生的にも良くなった

今後やりたいこと

  • 「プライベートスキーマ方式」と比較して気づいたこと
    • この方法ではハッシュ値を持っていた
    • ハッシュ値なので、値の内容自体はわからずとも、それらを使ってJOINとかはできる
    • ダイナミックデータマスキングは全部アスタリスクで埋めるので、それができなくなった
    • これらについて対処していきたい

  • もう少しモジュール化したいと考えている
    • 現在は各スキーマに対して1つのマスキングポリシーを使っている
    • そのポリシーを通過できるユーザーは、スキーマ内の全機密情報が閲覧できる
    • もう少し細かい粒度で設定できるようにすることも考えている
  • 例えば、同じスキーマ内に、名前に関するマスキングポリシーと、生年月日に関するマスキングポリシーの、2つを持つべきだと考えている
    • 必要なデータだけ閲覧できるようになる

データのセキュリティについて考えている企業へのアドバイス

  • Jetblue社の経験からアドバイスできること
    • プライベートスキーマ方式は絶対にオススメしない
    • プロジェクトのオーバーヘッドが増える
    • エンドユーザーが混乱する
    • dbtのコンパイル時間が増える
  • アスタリスク全埋めは避ける
    • MD5ハッシュを使うべき
  • 今回紹介するマクロは公開するから、それを使ってほしい
    • 車輪の再発明は不要

おわりに

Snowflakeのダイナミックデータマスキングの理解に役立ちました。そして、それをdbtで、作成されるテーブルに自動的に適用させるのは最先端な方式で、参考になりました。