🏂

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

2023/12/22に公開2

はじめに

個人情報保護の重要性が高まっています。昨今、どんな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でコード化することをおすすめします😃)

Snowflake Data Heroes

Discussion

oyoyoyoy

dbt_snow_maskというパッケージがあると思いますがそれを使用しないで自作マクロを作成したのはなぜなのでしょうか?

kaz3284kaz3284

メッセージありがとうございます!
単刀直入に回答するとタグベースのマスキングポリシーを設定するためにマクロで実施してます。
この実装を本番適用したのは2022年の前半でしたが、当時はSnowflakeでタグベースのマスキングポリシーがGAになった直後で、dbtのパッケージで対応できるとは思い当たらず自前のマクロ実装にしました。

あれから時間も経ってるので、dbt_snow_maskで対応できるかは検証してみたいと思います!