❄️

Snowflakeマスキングポリシーの罠:CTAS, CLONE, VIEW, MV, DTで個人情報は「どう」継承されるか

に公開

😱「完璧にマスクしたはずが、生データが漏洩していた…」

先日、私たちは動的データマスキング(DDM)を使って、個人情報(PII)をロールベースで保護する方法について記事にいたしました。
Snowflake「動的データマスキング」実践ガイド:Email・住所・電話番号のマスク方法を試してみた

ANALYST_ROLESELECT すると ****@example.com と表示され、MASK_ROLE なら生データが見える。「これで完璧だ!」と安心していました。

...しかし、ある日、悪夢が起こります。

MASK_ROLE を持つ開発者が、本番テーブルのコピーを作ろうとして、何気なく CTAS (Create Table As Select) を実行しました。

CREATE TABLE CUSTOMER_PII_BACKUP AS SELECT * FROM CUSTOMER_PII;

その後、ANALYST_ROLE の分析者が、新しくできた CUSTOMER_PII_BACKUP テーブルSELECT してみると...

そこには、すべての「生」の個人情報が、マスクされずに表示されていました。

「なぜだ!?」
「マスキングポリシーはテーブルに付いているはずじゃ?」

そう、これこそが Snowflake の DDM を運用する上で、最も危険な「継承」の罠 です。

DDM は万能ではありません。ポリシーが「いつ適用され」「いつ無視されるか」を理解していないと、意図せず生データをコピーし、セキュリティ事故を引き起こします。

この記事では、CTASCLONEVIEWMVDYNAMIC TABLE を実行した時、マスキングポリシーがどうなるのか、その挙動を徹底的に検証します。

🎭 大原則:「データ」と「ポリシー」は別物

まず、DDM の大原則を思い出しましょう。

DDM は、テーブルに保存されているデータ自体を書き換えるものではありません
SELECT クエリが実行された「瞬間」に、クエリを実行した人のロールを見て、結果セットを動的に書き換える機能です。

この「クエリの実行時」というのが最大のポイントです。

CTAS (CREATE TABLE ... AS SELECT) は何をしているでしょうか?

CTAS は、「SELECT クエリを実行し、その "結果" を新しい別のテーブルに "書き込む"」コマンドです。

つまり、MASK_ROLE(生データが見えるロール)の人が CTAS を実行すると、

  1. SELECT * が実行され、「生」データ の結果セットが返されます。
  2. その 「生」データ が、NEW_TABLE に書き込まれます。
  3. NEW_TABLE には、マスキングポリシーは一切適用されません

これが、データ漏洩のメカニズムです。

🛠️ 検証環境のおさらい

前回の記事と同じ環境を使います。

  • テーブル: CUSTOMER_PII
    • EMAIL 列に test_email_policy@より前を隠す)が適用済み。
  • ロール:
    • MASK_ROLE: CUSTOMER_PII の生データが見える(特権ロール)。
    • ANALYST_ROLE: CUSTOMER_PII を見ると EMAIL がマスクされる(一般ロール)。

🕳️ 【罠】ポリシーが継承されない操作

DDM の恩恵を受けられない、危険な操作から見ていきましょう。

罠1:CTAS (CREATE TABLE ... AS SELECT)

これが最も危険な罠です。CTAS はマスキングポリシーを継承しません

検証A:MASK_ROLE (特権ロール) で CTAS

USE ROLE MASK_ROLE; -- 生データが見えるロール

CREATE OR REPLACE TABLE PII_BACKUP_BY_ADMIN AS
SELECT * FROM CUSTOMER_PII;

検証B:ANALYST_ROLE (一般ロール) で CTAS

USE ROLE ANALYST_ROLE; -- マスクされたデータが見えるロール

CREATE OR REPLACE TABLE PII_BACKUP_BY_ANALYST AS
SELECT * FROM CUSTOMER_PII;

結果確認:ANALYST_ROLE で両方のテーブルを見る

-- (事前に PII_BACKUP_BY_ADMIN と PII_BACKUP_BY_ANALYST に対する
--  SELECT 権限が ANALYST_ROLE に付与されている前提)
USE ROLE ANALYST_ROLE;

-- 検証A (MASK_ROLE が作ったテーブル) を見る
SELECT EMAIL FROM PII_BACKUP_BY_ADMIN LIMIT 2;
-- 実行結果:
-- taro.tanaka@example.com
-- hanako.suzuki@example.jp
-- 
-- 😱 生データが漏洩している!ポリシーは付いていない!

-- 検証B (ANALYST が作ったテーブル) を見る
SELECT EMAIL FROM PII_BACKUP_BY_ANALYST LIMIT 2;
-- 実行結果:
-- ****@example.com
-- ****@example.jp
-- 
-- 😅 こちらはマスク「済み」のデータがコピーされている。

結論: CTAS は、実行したロールに見えている通りのデータを、ポリシー無しでコピーします。
つまり、新しく作成されたテーブルの列にはマスキングポリシーは付いておらず、「作成時に書き込まれた値」が常にそのまま表示されます。
管理者が CTAS を使うと、生データが漏洩します。

罠2:マテリアライズドビュー (MV) の罠 —「漏洩」ではなく「作成エラー」

「CTASがダメなら、MVでパフォーマンスを上げよう」と考えるかもしれません。
しかし、ここには別の罠(仕様上の制限)があります。

検証:MASK_ROLE (特権ロール) で MV を作成

USE ROLE MASK_ROLE; -- 生データが見えるロール

-- 「EMAIL」列(マスク適用済み)を含む MV を作成しようと試みる
CREATE OR REPLACE MATERIALIZED VIEW PII_MV AS
SELECT ID, EMAIL FROM CUSTOMER_PII WHERE ID < 3;

実行結果:

SQL compilation error:
Unsupported feature 'CREATE ON MASKING POLICY COLUMN'.

結論: CTAS とは異なり、Snowflake はこの操作を許可しません
ベーステーブルの列にマスキングポリシーが適用されている場合、その列を含むマテリアライズドビューを作成することは、仕様上ブロックされます。

罠3:動的テーブル (Dynamic Table) の罠

DYNAMIC TABLE (DT) は、MV とは異なり DDM との共存が可能ですが、CTAS と同様にポリシーを自動で継承しません

検証:MASK_ROLE (特権ロール) で DT を作成

USE ROLE MASK_ROLE; -- 生データが見えるロール
USE WAREHOUSE MY_WH;

-- 生データが見えるロールで DT を作成
CREATE OR REPLACE DYNAMIC TABLE PII_DT
  TARGET_LAG = '1 minute' -- LAG ではなく TARGET_LAG が正しい構文
  WAREHOUSE = MY_WH
  AS
    SELECT ID, EMAIL FROM CUSTOMER_PII WHERE ID < 3;

-- DT の初回リフレッシュを手動で実行
ALTER DYNAMIC TABLE PII_DT REFRESH;

結果確認:ANALYST_ROLEDT を見る

-- (事前に PII_DT に対する SELECT 権限が
--  ANALYST_ROLE に付与されている前提)
USE ROLE ANALYST_ROLE;

-- ANALYST_ROLE で DT をクエリ
SELECT EMAIL FROM PII_DT;
-- 実行結果:
-- taro.tanaka@example.com
-- hanako.suzuki@example.jp
--
-- 😱 ANALYST_ROLE なのに、生データが見えてしまった!

結論: DYNAMIC TABLE は、CTAS と同様にオーナー(作成者)の権限で見えるデータを物理的に実体化します。
ベーステーブルの DDM ポリシーは、新しく作られた DT の列には自動でコピーされません

✅ 安全な操作:ポリシーが正しく継承される方法

では、どうすれば安全にオブジェクトを複製・参照できるのでしょうか?

安全策1:CLONE (テーブルの完全複製)

テーブルのデータとメタデータ(ポリシー設定含む)を丸ごとコピーしたい場合は、CLONE を使います。

検証:MASK_ROLECLONE

USE ROLE MASK_ROLE; -- CLONE には元のテーブルの SELECT 権限が必要

-- CTAS ではなく CLONE を使用
CREATE OR REPLACE TABLE CUSTOMER_PII_CLONED
  CLONE CUSTOMER_PII;

結果確認:ANALYST_ROLECLONE されたテーブルを見る

-- (事前に CUSTOMER_PII_CLONED に対する SELECT 権限が
--  ANALYST_ROLE に付与されている前提)
USE ROLE ANALYST_ROLE;

SELECT EMAIL FROM CUSTOMER_PII_CLONED LIMIT 2;
-- 実行結果:
-- ****@example.com
-- ****@example.jp
--
-- 👍 安全!ポリシーがテーブルごと継承されている!

結論: CLONE はオブジェクトをそのまま複製します。DDM ポリシーも正しく継承(複製)されます。 PII テーブルのバックアップや開発環境への複製には CLONE が最適です。

安全策2:VIEW (標準ビュー)

VIEWSELECT クエリを保存する「ショートカット」であり、データを実体化しません。

検証:MASK_ROLEVIEW を作成

USE ROLE MASK_ROLE;

CREATE OR REPLACE VIEW PII_VIEW AS
SELECT ID, EMAIL FROM CUSTOMER_PII WHERE ID < 3;

結果確認:ANALYST_ROLEVIEW を見る

-- (事前に PII_VIEW に対する SELECT 権限が
--  ANALYST_ROLE に付与されている前提)
USE ROLE ANALYST_ROLE;

-- ANALYST_ROLE で VIEW をクエリ
SELECT EMAIL FROM PII_VIEW;
-- 実行結果:
-- ****@example.com
-- ****@example.jp
--
-- 👍 安全!ポリシーが正しく機能している!

結論: 標準ビューは、クエリが実行されるたびに、「ビューを叩いた人(ANALYST_ROLE)」の権限でベーステーブル(CUSTOMER_PII)の DDM ポリシーを評価します。データ共有の用途では非常に安全です。

📊 まとめ:DDM継承チートシート

Snowflake のオブジェクト操作と DDM の関係をまとめます。

操作 ポリシーは継承されるか? 実行者のロールの影響 データ漏洩リスク
CTAS No (継承されない) (実行者が見るデータが新テーブルに書き込まれる) 🔴 高い
CLONE Yes (継承される) ほぼ無し (メタデータごとコピー) 🟢 低い
CREATE VIEW N/A (ベーステーブルで評価) N/A (クエリ実行者のロールで動的に評価) 🟢 低い
CREATE MV N/A (作成自体がエラーになる) N/A (Snowflakeが漏洩をブロック) 🟡 中 (※設計変更を強いられる)
CREATE DYNAMIC TABLE No (継承されない) (オーナーが見るデータが実体化される) 🟠 注意 (※DT側にもマスクが別途必要)

😌 おわりに

動的データマスキング (DDM) は、クエリの結果セットに対して適用される「フィルター」のようなものです。
データそのものがマスクされているわけではない」という点を忘れてはいけません。

この特性を理解していないと、CTASCREATE DYNAMIC TABLE を特権ロールで実行した瞬間に、意図せず生データを漏洩させてしまいます

  • 安全なコピー: CLONE を使う。
  • 安全な共有: VIEW を使う。
  • 危険な操作: CTAS, DYNAMIC TABLE を生データが見えるロールで実行しない(実行する場合は、作成後のオブジェクトにもDDMポリシーを適用する)。
  • ブロックされる操作: MATERIALIZED VIEW はマスクされた列には作成できない。(パフォーマンスとDDMを両立させたい場合は DYNAMIC TABLE を検討する)

「ポリシーを付けたから安心」ではなく、「ポリシーがどう動くか」を理解することが、Snowflake のセキュリティを守る上で最も重要ですね。

📚 参考出典

Discussion