機微なカラム — SSN、クレジットカード、メール、住所 — はサポート、分析、開発のためにクエリ可能であり続けなければなりません。広範な平文アクセスは答えではありません。データマスキングが答えです。GDPR、HIPAA、PCI などのワークロードでは、マスキングは法的要件でもあります。
BigQuery は動的データマスキングを、Dataplex Universal Catalog で管理されるポリシータグの上に構築されたカラムレベルセキュリティの一部として同梱しています。Bytebase Dynamic Data Masking はその前段に位置します。すべての BigQuery データセットで、そしてフリート内のあらゆる他エンジンでも、1 つのポリシーモデル。申請。レビュー。承認。本記事で両者を比較します。
BigQuery Dynamic Data Masking
動的データマスキングは GA です。カラムレベルセキュリティを拡張するもので、別途のライセンス料金はかかりません — すべての BigQuery プロジェクトで利用できます。(Snowflakeはマスキングを Enterprise へのアップグレードの背後にゲートしますが、それとは対照的です。)1 つ注意点があります: 特定の BigQuery エディションで作成されたリザベーションではマスキングが適用されない場合があるため、スロット構成を確認してください。
マスキングは 3 つのステップで構成します:
- ポリシータグのタクソノミーを作成する。
- ポリシータグをカラムに付与する。
- マスキングルールと一連のプリンシパルをそのタグにバインドするデータポリシーを定義する。
読み取り時、BigQuery は Masked Reader ロールを持つプリンシパルに対してカラムを書き換えます。保存されたデータは変更されません。
BigQuery は組み込みのマスキングルールのセットを同梱しており、それらでカバーできないものにはカスタムルーチンが使えます。同じ 3 ステップをコマンドで示します:
# 1. タクソノミーとポリシータグを作成(Dataplex Universal Catalog)。
gcloud data-catalog taxonomies create \
--location=us --display-name=pii
gcloud data-catalog taxonomies policy-tags create \
--location=us --taxonomy=$TAXONOMY_ID --display-name=ssn
# 2. テーブルスキーマを通じてポリシータグをカラムに付与。
# schema.json:
# [{ "name": "ssn", "type": "STRING",
# "policyTags": { "names": ["projects/$PROJECT/locations/us/taxonomies/$T/policyTags/$P"] } }]
bq update --schema schema.json $PROJECT:sales.customers
# 3. マスキングルールをポリシータグにバインドするデータポリシーを作成。
# これには SQL DDL も bq コマンドも無い — Data Policy API を使う。
curl -X POST \
"https://bigquerydatapolicy.googleapis.com/v1/projects/$PROJECT/locations/us/dataPolicies" \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{
"dataPolicyId": "ssn_mask",
"dataPolicyType": "DATA_MASKING_POLICY",
"policyTag": "projects/'"$PROJECT"'/locations/us/taxonomies/'"$T"'/policyTags/'"$P"'",
"dataMaskingPolicy": { "predefinedExpression": "LAST_FOUR_CHARACTERS" }
}'
# 4. プリンシパルに Masked Reader ロールを付与。
gcloud projects add-iam-policy-binding $PROJECT \
--member="user:analyst@example.com" \
--role="roles/bigquerydatapolicy.maskedReader"コンソールは Manage Data Policies で同じフローを実行します。policy-as-code には、Terraform の google_bigquery_datapolicy_data_policy リソースがステップ 3 をラップします。
権限: 3 つの状態
ポリシータグの付いたカラムへのアクセスは、プリンシパルが持つ IAM ロールによって、3 つの状態のいずれかに解決されます:
- Fine-Grained Reader(
datacatalog.categoryFineGrainedReader)— 平文が見える。このロールはマスキングの上位に位置する。付与は最小限にし、変更を監査する。 - Masked Reader(
bigquerydatapolicy.maskedReader)— データポリシーが定義するマスク値が見える。 - いずれも持たない — クエリは拒否される。カラムレベルセキュリティが読み取りそのものをブロックする。
カラムは 1 つのポリシータグを持ち、そのタグは単一のマスキングルールにマッピングされます。マスキングはカラムごと、タグごとです — その上に重ねる第 2 のルールはありません。
BigQuery のデータマスキングがしないこと
- Fine-Grained Reader を止める。 このロールはマスクされていない値を返します。単なる IAM 付与なので、持つ者を制限し、付与を監査してください。
- カラムごとに複数のルールを適用する。 カラムごとに 1 つのポリシータグ。タグのデータポリシーは 1 つのマスキングルールを選びます。合成はできません。
- 直接アクセスやサービスアクセスをレビューに通す。 BI ツール、スケジュールジョブ、アプリケーションのサービスアカウントは、自身が持つロールのままウェアハウスをクエリします。Fine-Grained Reader を付与されたサービスアカウントは、申請もレビューもなく平文を読みます。
- 承認ワークフローやアンマスクの監査証跡を提供する。 Fine-Grained Reader の付与は IAM 編集です。申請・レビュー・承認の経路はなく、その付与はファーストクラスの監査対象マスキングイベントではありません。
- レガシー SQL、ワイルドカード(
*)テーブルクエリ、コピージョブで動作する。 マスキングはこれら 3 つすべてと互換性がありません。 - カスタムルーチンでパーティショニング/クラスタリングカラムをマスクする。 カスタムマスキングルーチンは、パーティショニングまたはクラスタリングに使われるカラムには適用されません。
- 行をフィルタする。 マスキングはカラムレベルです。行レベルの制御には、行レベルセキュリティ(row access policy)を使ってください。
Bytebase Dynamic Data Masking

ネイティブマスキングには文書化された隙があります。Fine-Grained Reader の付与は平文を返し、それは単なる IAM 編集です — 申請なし、レビューなし、監査されたアンマスクイベントなし。アクセスの付与と、誰が何を見たかの証明は、別々の手動ステップです。原因は構造的です: マスキングはウェアハウス内部でカラムを書き換えますが、それをバイパスするロール付与は上流の IAM にあります。隙を塞ぐには、カラムだけでなくクエリそのものを統制する必要があります。
Bytebase Dynamic Data Masking はクエリを統制します。クエリは Bytebase の SQL Editor を経由します。Bytebase は結果をエディタから出る前にマスクします。バックエンドのプロジェクトで Fine-Grained Reader を付与されていてもポリシーをバイパスできません。アンマスクはアクセス判断になります — Query 権限の付与は組み込みのワークフロー — 申請。レビュー。承認。 — を通り、すべてのステップが監査されます。
ポリシーは固定の優先順位で評価される 3 つのレイヤーから構成されます: マスキング例外 > グローバルマスキングルール > カラムマスキング。
- グローバルマスキングルール。 ワークスペース単位。ルールは上から評価され、最初に一致したものが勝ちます。一致条件は環境、プロジェクト、データベース、データ分類にまたがります。一致するたびに Semantic Type が適用され、それがマスキングアルゴリズム(full、partial、MD5、range、カスタム)を選びます。

- カラムマスキング。 プロジェクト単位のオーバーライド。グローバルルールが該当しない特定カラムに適用します。

- マスキング例外。 指名されたユーザーが特定のデータベースまたはテーブルに対して期限付きの
QueryまたはExport例外を受けます。サービスアカウントは対象外。すべての付与とすべてのアクセスが記録されます。

マスキングは伝播します。カラムがマスクされると、そのポリシーは依存するすべてのビューと派生構造に及びます。マスク済みカラム上の式もマスクされたままです。

ポリシーは GitOps でコード化することもできます。
マスキングの決定は監査ログに記録されます。SQL 実行エントリは、マスクされたカラム、Semantic Type、一致したルールというカラム単位のマスキングメタデータを、ユーザー、送信元 IP、ステートメント、行数とともに記録します。例外の付与、例外の行使、ポリシーの編集はファーストクラスの監査イベントです。アクセス判断と強制の証跡は同じレコードに収まります。
強制の境界: Bytebase は SQL Editor を経由するクエリをマスクします。BigQuery に直接到達するトラフィックはバイパスし — BI ツール、スケジュールジョブ、サービスアカウント — そこはネイティブのデータマスキングと IAM がカバーします。パターンは対称です — ウェアハウスではネイティブマスキング、承認と監査が要る人間のクエリ経路では Bytebase。1 つのポリシーが BigQuery と、その隣にある Postgres、MySQL、SQL Server、Oracle、Snowflake のインスタンスに適用されます。
比較
| BigQuery Dynamic Data Masking | Bytebase Dynamic Data Masking | |
|---|---|---|
| 互換性 | BigQuery のみ | BigQuery を含むすべてのエンジン ⭐️ |
| メカニズム | カラムごとのポリシータグ + データポリシー ⭐️ | Bytebase 内のポリシー、SQL Editor で適用 |
| 強制位置 | ウェアハウス、全読み取り経路 ⭐️ | SQL Editor |
| マスキングルール | 組み込みルール + カスタムルーチン ⭐️ | full、partial、MD5、range、カスタム |
| ポリシー管理 | コンソール / Data Policy API / Terraform | 集中 UI、付与、監査ログ ⭐️ |
| 権限スコープ | カラム(ポリシータグ 1 つ) | プロジェクト、データベース、テーブル、カラム ⭐️ |
| ワークフロー | IAM 付与のみ | 申請。レビュー。承認。 ⭐️ |
| 行レベル制御 | 無し(row access policy と併用) | 無し(アクセスポリシーと併用) |
| コスト | BigQuery に含まれる ⭐️ | Bytebase Enterprise |
選び方
- 単一の BigQuery エステート。クライアントを問わずマスキングを強制する必要がある場合。 ネイティブのデータマスキングを使う。ウェアハウス内、全読み取り経路 — 直接接続する BI ツール、スケジュールジョブ、サービスアカウントも含めて。行レベルの制御には row access policy を併用する。
Fine-Grained Readerの付与は少数に留め、ログに記録する。 - 承認ワークフローとアンマスクの監査証跡が必要な場合。 ネイティブマスキングの付与は単なる IAM 編集です。人間のクエリ経路には Bytebase を使う — 申請。レビュー。承認。 — すべての例外とアクセスをログに記録。
- 混在フリート — BigQuery を Postgres、MySQL、SQL Server、Snowflake と併用する場合。 Bytebase を使う。1 つのポリシーモデル。すべてのエンジン。すべてのアンマスクに監査付きの付与、アクセスログと同じ場所に記録。
- 両方。 ウェアハウスではネイティブマスキング — 直接接続、BI ツール、サービスアカウント。Bytebase は人間のクエリ経路を SQL Editor 経由で統制し、承認と監査を提供します。両者は組み合わさります。
このチュートリアルで Bytebase Dynamic Data Masking を試せます。