はじめに
皆さんSnowflakeのマスキングポリシーは活用していますか?
マスキングポリシーは、ポリシーが紐づけられたカラムのデータを、別の意味を持つ文字列などに置き換えて表示させることができる仕組みです。
実際に利用する際は、マスキングポリシーを作成し、作成したポリシーをカラムやタグに紐づけることで利用することができます。
カラム(列)に対する制御であることから、列レベルのセキュリティに分類されます。
また、このマスキング方式は動的マスキングとなっており、実際のデータをマスキングデータで上書きするものではありません。
よくあるロールでの制御
マスキングポリシーでは、条件式を使った判定によるマスキングの制御をサポートしています。
よくあるのは、特定の社員や部署にのみ非マスキング状態のデータを閲覧させたいといったケースや、逆に特定の社員や部署にのみマスキング状態のデータを閲覧させたいといったケースです。
こうした場合、条件式にロールによる判定を入れることで上記のケースに対応することができます。
実際にやってみる
次にSnowflakeのベストプラクティスである、ユーザーの役割に応じた機能ロールと、権限セットのアクセスロールを作成します。
アクセスロールは機能ロールに紐づけます。
現時点では、以下のような紐づきになっています。
[ユーザー] <- [機能ロール] <- [アクセスロール]
次にマスキングポリシーを作成します。
今回のポリシーは、AR_masking_roleというロール名がセッションが保持するロールに含まれている場合は、マスキング後の文字列を返し、それ以外は元の値を返すという設定にしています。

次に、作成したマスキングポリシーをテーブルのカラムに紐づけます。

これで、マスキングポリシーの設定は完了です。
それでは、ユーザーに紐づけた機能ロールに切り替えてクエリを実行します。
結果を見るとマスキングされていることがわかります。

このような現象が発生するのはなぜか?
このような事象が発生する原因は、マスキングポリシーに設定した条件式と、Snowflakeの権限管理によるものです。
マスキングポリシーで設定したIS_ROLE_IN_SESSION()という関数は、プライマリーロールまたはセカンダリーロールに指定したロールが含まれているか確認する関数です。
先ほどは、USE ROLE FR_masking_role;でスイッチしたので、プライマリーロールはFR_masking_roleです。
つまり、セカンダリーロールにAR_masking_roleが含まれているということがわかります。
ただ、厄介なことに、ロールとロールを関連付けた場合、下位のロールはセカンダリーロール一覧を取得する関数で表示されません。
こちらはドキュメントにも記載があります。

事故は起こらないか?
マスキングポリシーや行アクセスポリシーなど、重要なデータを秘匿化するための機能は設定次第で事故を起こす可能性は大いにあります。
たとえば、機能ロールで、部長用ロール <- 課長用ロール <- 一般職員用ロールというように、部署を取りまとめるためにこのようなロール構成にしたとしましょう。
一般職員は個人情報を閲覧することを禁止するようにマスキングポリシーを作成して適用します。
本記事で記載したIS_ROLE_IN_SESSION()で、マスキングを適用させるようなポリシーかつ、アカウント全体でセカンダリーロールが有効化されている構成においては、上位に位置している部長用ロールや課長用ロールも一般職員用ロールを継承しているため、マスキングポリシーによってマスキング後のデータしか閲覧できなくなります。
事故を起こさないために
まず、マスキングポリシーや行アクセスポリシーは、アクセスロールに焦点を当てるのではなく、「役割に応じて制御する」と考えたほうがしっくりきます。
アクセスロールに対して制御をする場合、上記の通り、セカンダリーロールの関係性や、視覚的にわかりづらくなる傾向にあります。
そのため、CURRENT_ROLE()関数を使ってユーザーのプライマリーロールによって制御をするといった方式も安全策の一つと言えるでしょう。
おわりに
マスキングポリシーでロールによる判定を含める場合、意外に考えることが多くなることがわかりました。
特に、Snowflakeのベストプラクティスである機能ロールとアクセスロールの考え方、プライマリーロールとセカンダリーロールの考え方を知っておかないと思わぬ落とし穴にはまってしまうことがあります。
皆さんもこの機会にロールの考え方や、アクセス制御の考え方をアップデートしてみましょう!

