🏂

dbtによるタグベースのマスキングポリシーの実装

2023/12/22に公開

はじめに

個人情報保護の重要性が高まっています。昨今、どんなDWHにおいてもユーザに割り当てられた権限(ロール)によって見えるデータをコントロールすることは重要な機能となっています。

Snowflakeではそのような情報保護をダイナミックデータマスキングで実現可能です。

ここではダイナミックデータマスキングを、タグベースのマスキングポリシーの適用とdbtよってよりシンプルに運用する実装を紹介します😃

タグベースのマスキングポリシーをdbtで実装すると、yamlファイルへ記述のみでシンプルかつ柔軟にダイナミックデータマスキングを運用することができます!

dbtプロジェクトのコードについて

以下の公開リポジトリにアップしました。適宜ご参照ください。
https://github.com/kaz3284/dbt_project_example

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

クエリの実行時に、実行ユーザに紐づいた権限(ロール)によって表示するデータを動的に制御する仕組みです。
cf.https://docs.snowflake.com/ja/user-guide/security-column-ddm-intro

具体的には、イメージのように異なるロールでクエリを実行した場合、同じクエリでも権限に応じて見えるデータを動的に変えることができます。

具体例

セキュリティレベル0,1,2の三段階、マスク方法にhash化、匿名化の二通りを適用すると以下のようになります。

  • レベル0のロール(デフォルト閲覧権限)にて実行すると、
    ACCOUNT_ID, COUNTRY列以外に対してマスク化(hash、匿名)が施されています。

  • レベル1のロール(レベル1の閲覧権限)にて実行すると、
    レベル1はレベル0に加えて、PREFECTURE,CITYの閲覧権限が付与されているため、これらの列のrawdataが見えるようになります。

  • レベル2のロール(レベル2の閲覧権限)にて実行すると、
    レベル2は最上位で全列の閲覧権限を有します、全列のrawdataが見れるようになります。

タグベースのマスキングポリシーとは

ダイナミックデータマスキングをより効率的に運用できるよう、オブジェクト(table, column)に付与できるタグを使って設定できるようにしたものです。
cf. https://docs.snowflake.com/ja/user-guide/tag-based-masking-policies

これによりオブジェクトが増えても、管理がよりシンプルになります。

WebUI(Snowsight)では、上のように表示され、編集もできます。

以後、dbtを使ったタグベースのマスキングポリシーについて実装例とともに紹介していきます。

dbtとは

なかなか簡潔に言い表すのは難しいところですが、ソースデータの取り込みから始まるデータ分析システム(OLAP)において、データ処理のフレームワークを提供してくれるツールと言えます。
cf.https://docs.getdbt.com/

OLAPでは取り込んだソースデータをそのまま使えばOKというユースケースは少なく、様々な種類のデータと結合して集計(バッチ処理)したり、後からデータ変更を終えるよう履歴を残したり(Snapshot)、様々な変換(Transform)処理が必要です。

そのような処理を定型化して実装すること可能にする仕組みや、処理後の品質の確認(test)、変換処理の依存関係の可視化(linage)、メタ情報のdocs化…etc、OLAPを効率的に開発できるようサポートする機能を豊富に持ちます。

dbtを使ったタグベースのマスキングポリシー実装について

ざっくり概要を書き出すと以下の三段階で完結します。

  1. (初回もしくは設定変更時のみ) Snowflakeにてタグとマスキングポリシーを設定する。
  2. dbtのschema定義(yaml)にて、タグの設定を記述する
  3. dbt build(run)を実行する。(マスキング設定が適用される)

以上。初期設定を済ませれば、以後はyamlの記述だけで運用可能です。

dbtでの実装について

ここで紹介する実装は↓へアップました。
https://github.com/kaz3284/dbt_project_example/tree/main/dbt

  • スキーマ設定
    モデルのカラム定義に以下のようにタグを記述する形になります。
    工夫したポイントは、タグのnameでセキュリティレベルの設定、valueでマスキング方法の設定ができるようになっています
      columns:
     - name: prefecture
       meta:
         tag:
           - type: masking_policy
             name: masking_policy_secure_level_1 #レベル1の情報保護
             value: mask_hash #hash化:解読はできないが結合は可能
     - name: city
       meta:
         tag:
           - type: masking_policy
             name: masking_policy_secure_level_1 #レベル1の情報保護
             value: mask_hash #hash化:解読はできないが結合は可能
     - name: zip
       meta:
         tag:
           - type: masking_policy
             name: masking_policy_secure_level_2 #レベル2の情報保護
             value: mask_anonymous #匿名化:解読も結合も不可能
    # タグベースマスキングポリシー適用のためのマクロ起動設定
    post-hook:
    - "{{ apply_tag_setting() }}"

Snowflakeへの適用(初回もしくは、設定変更時)について

  • まずタグを作成します
  CREATE SCHEMA tags;
  CREATE TAG MASKING_POLICY_SECURE_LEVEL_1;
  CREATE TAG MASKING_POLICY_SECURE_LEVEL_2;
  SHOW TAGS;
  • 次にマスキングポリシーを作成します
 --- SECURE_LEVEL_1の文字列型カラムに対するマスポリ設定(レベル2権限を持つ者はレベル1データも見れる)
  create masking policy SECURE_LEVEL_1_STRING as (val string) returns string ->
  case
    -- ロール名によって判別
    when current_role() in ('SECURE_LEVEL_1','SECURE_LEVEL_2') then val
    -- 値が空の場合はそのまま返す
    when length(val) = 0 then val
    -- tag値(マスキング方式)がhashの場合はhash値を返す。
    when system$get_tag_on_current_column('MASKING_POLICY_SECURE_LEVEL_1') = 'mask_hash' then sha2(val)
    -- tag値(マスキング方式)が上記意外の場合は「***」(匿名値)を返す
    else '***'
  end;
  --- SECURE_LEVEL_2の文字列型カラムに対するマスポリ設定
  create or replace masking policy SECURE_LEVEL_2_STRING as (val string) returns string ->
  case
    -- ロール名によって判別
    when current_role() in ('SECURE_LEVEL_2') then val
    -- 値が空の場合はそのまま返す
    when length(val) = 0 then val
    -- tag値(マスキング方式)がhashの場合はhash値を返す。
    when system$get_tag_on_current_column('MASKING_POLICY_SECURE_LEVEL_2') = 'mask_hash' then sha2(val)
    -- tag値(マスキング方式)が上記意外の場合は「***」(匿名値)を返す
    else '***'
  end;
  • 最後にマスキングポリシーとタグの紐付けを設定します
  --- tagに対してマスキングポリシーを紐づける。
 alter tag MASKING_POLICY_SECURE_LEVEL_1 set
   masking policy tags.SECURE_LEVEL_1_STRING;
 alter tag MASKING_POLICY_SECURE_LEVEL_2 set
   masking policy tags.SECURE_LEVEL_2_STRING;

さいごに

私のプロジェクトでは、dbtにてデータモデルを運用しています。タグベースのマスキングポリシーがGAとなった少し後くらいから運用していますが、非常に快適に運用できています。

快適さの要因としては、最初にタグとマスキングポリシーの定義さえ設定してしまえば、タグの追加や更新はyamlを更新するだけというシンプルな運用にできるところにあります。

WebUI(Snowsight)からも設定可能なのでdbtを使わなくても運用可能です(運用の安定さを気にするならdbtやTerraformでコード化することをおすすめします😃)

Discussion