Cloud KMS 鍵によるデータの保護

デフォルトでは、BigQuery は格納するお客様のコンテンツを保存時に暗号化します。BigQuery では、こうしたデフォルトの暗号化の処理と管理が自動的に行われるため、ユーザー側での操作は不要です。まず、BigQuery テーブルのデータは、「データ暗号鍵」を使用して暗号化されます。さらに、このデータ暗号鍵が「鍵暗号鍵」で暗号化されます。これはエンベロープ暗号化と呼ばれます。鍵暗号鍵ではデータは直接暗号化されませんが、データの暗号化に使用するデータ暗号鍵を暗号化するために使用されます。詳細については、鍵管理をご覧ください。

ユーザー自身で暗号化を制御する場合は、BigQuery の顧客管理の暗号鍵(CMEK)を使用できます。この場合、データを保護する鍵暗号鍵を Google が管理するのではなく、ユーザーが Cloud KMS で制御および管理します。このトピックでは、この手法について詳しく説明します。

詳細については、Google Cloud での暗号化オプションをご覧ください。

始める前に

  1. データセットテーブルクエリについて理解します。

  2. BigQuery と Cloud KMS を同じ Google Cloud プロジェクトで実行するか、別のプロジェクトで実行するかを決定します。以下の説明では次の表記方法を用います。

    • PROJECT_ID: BigQuery を実行するプロジェクトのプロジェクト ID
    • PROJECT_NUMBER: BigQuery を実行するプロジェクトのプロジェクト番号
    • KMS_PROJECT_ID: Cloud KMS を実行するプロジェクトのプロジェクト ID(BigQuery を実行するプロジェクトと同じプロジェクトの場合でも)
    Google Cloud プロジェクト ID とプロジェクト番号については、プロジェクトの識別をご覧ください。

  3. 新しいプロジェクトでは、BigQuery が自動的に有効になります。既存のプロジェクトを使用して BigQuery を実行する場合は、BigQuery API を有効化します。

  4. Cloud KMS を実行する Cloud プロジェクトでは、次の操作を行います。

    1. Cloud Key Management Service API を有効にします
    2. キーリングと鍵の作成の説明に従って、キーリングと鍵を作成します。キーリングは、BigQuery データセットのロケーションと一致するロケーションで作成します。
      • マルチリージョン データセットでは、一致するロケーションからのマルチリージョンのキーリングを使用する必要があります。たとえば、リージョン US 内のデータセットはリージョン us のキーリングで保護する必要があり、リージョン EU 内のデータセットはリージョン europe のキーリングで保護する必要があります。
      • リージョン データセットでは、一致するリージョンの鍵を使用する必要があります。たとえば、リージョン asia-northeast1 内のデータセットはリージョン asia-northeast1 のキーリングで保護する必要があります。
      • BigQuery では global リージョンの使用をサポートしていません。
      BigQuery と Cloud KMS がサポートされるロケーションの詳細については、クラウドのロケーションをご覧ください。

暗号化仕様

BigQuery でデータを保護するために使用される Cloud KMS 鍵は、AES-256 鍵です。これらの鍵は、BigQuery で鍵暗号鍵として使用され、データを暗号化するデータ暗号鍵を暗号化します。

暗号化と復号の権限を付与する

CMEK 鍵で BigQuery データを保護するには、その鍵を使用して暗号化と復号を行う権限を BigQuery サービス アカウントに付与します。Cloud KMS 暗号鍵の暗号化 / 復号のロールによってこの権限を付与できます。

Google Cloud Console を使用して BigQuery サービス アカウント ID を特定し、サービス アカウントに、Cloud KMS を使用して暗号化および復号する適切なロールを付与します。

サービス アカウント ID を特定する

BigQuery サービス アカウント ID の形式を次に示します。

bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com

次の方法により、プロジェクトの BigQuery サービス アカウント ID が特定できます。

Console

  1. Cloud Console で [ダッシュボード] ページに移動します。

    [ダッシュボード] ページに移動する

  2. ページ上部の [選択元] プルダウン リストをクリックします。表示される [選択元] ウィンドウで、プロジェクトを選択します。

  3. プロジェクト ID とプロジェクト番号はどちらも、プロジェクト ダッシュボードの [プロジェクト情報] カードに表示されます。

    プロジェクト情報カード

  4. 次の文字列で、PROJECT_NUMBER をプロジェクト番号に置き換えたものが、BigQuery サービス アカウント ID になります。

    bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com
    

bq

bq show コマンドに --encryption_service_account フラグを指定して、サービス アカウント ID を確認します。

bq show --encryption_service_account

このコマンドを実行すると、サービス アカウント ID が表示されます。

                  ServiceAccountID
-------------------------------------------------------------
bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com

暗号化 / 復号のロールを割り当てる

クリップボードにコピーした BigQuery システムのサービス アカウントに Cloud KMS 暗号鍵の暗号化 / 復号のロールを割り当てます。このアカウントの形式は次のとおりです。

bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com

Console

  1. Cloud Console で [暗号鍵] ページを開きます。

    [暗号鍵] ページを開く

  2. 鍵を含むキーリングの名前をクリックします。

  3. ロールを追加する暗号鍵のチェックボックスをクリックします。[権限] タブが開きます。

  4. [メンバーを追加] をクリックします。

  5. サービス アカウントのメールアドレス(bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com)を入力します。

    • このサービス アカウントがすでにメンバーリストに含まれている場合、サービス アカウントには既存のロールがあります。bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com サービス アカウントの現在のロールのプルダウン リストをクリックします。
  6. [ロールを選択] プルダウン リストをクリックし、[Cloud KMS] をクリックして、[Cloud KMS 暗号鍵の暗号化 / 復号] ロールをクリックします。

  7. [保存] をクリックして、bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com サービス アカウントにロールを適用します。

gcloud

ロールを割り当てるには、gcloud コマンドライン ツールを使用できます。

gcloud kms keys add-iam-policy-binding \
--project=KMS_PROJECT_ID \
--member serviceAccount:bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com \
--role roles/cloudkms.cryptoKeyEncrypterDecrypter \
--location=KMS_KEY_LOCATION \
--keyring=KMS_KEY_RING \
KMS_KEY

以下を置き換えます。

  • KMS_PROJECT_ID: Cloud KMS を実行している Google Cloud プロジェクトの ID
  • PROJECT_NUMBER: BigQuery を実行している Cloud プロジェクトのプロジェクト番号(プロジェクト ID ではありません)
  • KMS_KEY_LOCATION: Cloud KMS 鍵のロケーション名
  • KMS_KEY_RING: Cloud KMS 鍵のキーリング名
  • KMS_KEY: Cloud KMS 鍵の名前。

鍵のリソース ID

このトピックの例に示すように、CMEK を使用するには Cloud KMS 鍵のリソース ID が必要です。この鍵は次の形式で、大文字と小文字は区別されます。

projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY

鍵のリソース ID を取得する

  1. Cloud Console で [暗号鍵] ページを開きます。

    [暗号鍵] ページを開く

  2. 鍵を含むキーリングの名前をクリックします。

  3. リソース ID を取得したい鍵について、その他アイコン をクリックします。

  4. [リソース ID をコピー] をクリックします。鍵のリソース ID がクリップボードにコピーされます。

Cloud KMS で保護されるテーブルを作成する

Cloud KMS で保護される空のテーブルを作成する

Cloud KMS で保護されるテーブルを作成するには、以下のようにします。

Console

  1. Cloud Console で [BigQuery] ページを開きます。

    [BigQuery] ページに移動

  2. ナビゲーション パネルの [リソース] セクションでプロジェクトを展開し、データセットを選択します。

  3. [テーブルを作成] をクリックします。

  4. [Create table] ページで、スキーマ定義を持つ空のテーブルを作成するために必要な情報を入力します。[テーブルを作成] をクリックする前に、暗号化の種類を設定し、テーブルで使用する Cloud KMS 鍵を指定します。

    1. [詳細オプション] をクリックします。
    2. [お客様が管理する鍵] をクリックします。
    3. 鍵を選択します。使用する鍵がリストにない場合は、鍵のリソース ID を入力します。
  5. [テーブルを作成] をクリックします。

bq

テーブルの作成には、--destination_kms_key フラグを指定した bq コマンドライン ツールを使用できます。--destination_kms_key フラグで、テーブルで使用する鍵のリソース ID を指定します。

スキーマを使用して空のテーブルを作成するには、次のコマンドを実行します。

bq mk --schema name:string,value:integer -t \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
mydataset.newtable

また、DDL ステートメントを使用することもできます。

bq query --use_legacy_sql=false "
  CREATE TABLE mydataset.newtable (name STRING, value INT64)
  OPTIONS(
    kms_key_name='projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY'
  )
"

クエリからテーブルを作成するには、次のコマンドを実行します。

bq query --destination_table=mydataset.newtable \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
"SELECT name,count FROM mydataset.babynames WHERE gender = 'M' ORDER BY count DESC LIMIT 6"

bq コマンドライン ツールの詳細については、bq コマンドライン ツールの使用をご覧ください。

Go

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// createTableWithCMEK demonstrates creating a table protected with a customer managed encryption key.
func createTableWithCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydatasetid"
	// tableID := "mytableid"
	ctx := context.Background()

	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	tableRef := client.Dataset(datasetID).Table(tableID)
	meta := &bigquery.TableMetadata{
		EncryptionConfig: &bigquery.EncryptionConfig{
			// TODO: Replace this key with a key you have created in Cloud KMS.
			KMSKeyName: "projects/cloud-samples-tests/locations/us/keyRings/test/cryptoKeys/test",
		},
	}
	if err := tableRef.Create(ctx, meta); err != nil {
		return err
	}
	return nil
}

Java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardSQLTypeName;
import com.google.cloud.bigquery.StandardTableDefinition;
import com.google.cloud.bigquery.TableDefinition;
import com.google.cloud.bigquery.TableId;
import com.google.cloud.bigquery.TableInfo;

// Sample to create a cmek table
public class CreateTableCMEK {

  public static void runCreateTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KEY_NAME";
    Schema schema =
        Schema.of(
            Field.of("stringField", StandardSQLTypeName.STRING),
            Field.of("booleanField", StandardSQLTypeName.BOOL));
    // i.e. projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{cryptoKey}
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    createTableCMEK(datasetName, tableName, schema, encryption);
  }

  public static void createTableCMEK(
      String datasetName, String tableName, Schema schema, EncryptionConfiguration configuration) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);
      TableDefinition tableDefinition = StandardTableDefinition.of(schema);
      TableInfo tableInfo =
          TableInfo.newBuilder(tableId, tableDefinition)
              .setEncryptionConfiguration(configuration)
              .build();

      bigquery.create(tableInfo);
      System.out.println("Table cmek created successfully");
    } catch (BigQueryException e) {
      System.out.println("Table cmek was not created. \n" + e.toString());
    }
  }
}

Python

新しいテーブルを顧客管理の暗号鍵で保護するには、Table.encryption_configuration プロパティを EncryptionConfiguration オブジェクトに設定してからテーブルを作成します。

# from google.cloud import bigquery
# client = bigquery.Client()
# dataset_id = 'my_dataset'

table_ref = dataset.table("my_table")
table = bigquery.Table(table_ref)

# Set the encryption key to use for the table.
# TODO: Replace this key with a key you have created in Cloud KMS.
kms_key_name = "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format(
    "cloud-samples-tests", "us", "test", "test"
)
table.encryption_configuration = bigquery.EncryptionConfiguration(
    kms_key_name=kms_key_name
)

table = client.create_table(table)  # API request

assert table.encryption_configuration.kms_key_name == kms_key_name

Cloud KMS 鍵で保護されたテーブルのクエリ

Cloud KMS で保護されたテーブルをクエリするために、特別な方法は必要ありません。テーブルのコンテンツの暗号化に使用された鍵の名前が BigQuery に保存され、Cloud KMS で保護されたテーブルに対してクエリが実行されるとその鍵が使用されます。

そのテーブルのコンテンツの暗号化に使用された Cloud KMS 鍵に BigQuery がアクセスできれば、既存のすべてのツール、BigQuery コンソール、bq コマンドライン ツールは、デフォルトの暗号化されたテーブルの場合と同様に実行されます。

Cloud KMS 鍵を使用したクエリ結果の保護

Console

  1. Cloud Console で [BigQuery] ページを開きます。

    [BigQuery] ページに移動

  2. [クエリを新規作成] をクリックします。

  3. 有効な BigQuery SQL クエリをクエリテキスト領域に入力します。

  4. [その他] をクリックし、[クエリの設定] をクリックして、[詳細オプション] をクリックします。

  5. [顧客管理の暗号化] を選択します。

  6. 鍵を選択します。使用する鍵がリストにない場合は、鍵のリソース ID を入力します。

  7. [保存] をクリックします。

  8. [実行] をクリックします。

bq

Cloud KMS 鍵を使用して宛先テーブルやクエリ結果(一時テーブルを使用している場合)を保護するには、--destination_kms_key フラグを指定します。--destination_kms_key フラグで、宛先または結果テーブルで使用する鍵のリソース ID を指定します。

--destination_table フラグを使用して、クエリ結果の宛先を指定することもできます。--destination_table を使用しなかった場合、クエリ結果は一時テーブルに書き込まれます。

テーブルをクエリするには、次のコマンドを実行します。

bq query \
--destination_table=mydataset.newtable \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
"SELECT name,count FROM mydataset.babynames WHERE gender = 'M' ORDER BY count DESC LIMIT 6"

bq コマンドライン ツールの詳細については、bq コマンドライン ツールの使用をご覧ください。

Go

import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/bigquery"
	"google.golang.org/api/iterator"
)

// queryWithDestinationCMEK demonstrates saving query results to a destination table and protecting those results
// by specifying a customer managed encryption key.
func queryWithDestinationCMEK(w io.Writer, projectID, dstDatasetID, dstTableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	q := client.Query("SELECT 17 as my_col")
	q.Location = "US" // Location must match the dataset(s) referenced in query.
	q.QueryConfig.Dst = client.Dataset(dstDatasetID).Table(dstTableID)
	q.DestinationEncryptionConfig = &bigquery.EncryptionConfig{
		// TODO: Replace this key with a key you have created in Cloud KMS.
		KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/test",
	}
	// Run the query and print results when the query job is completed.
	job, err := q.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}
	if err := status.Err(); err != nil {
		return err
	}
	it, err := job.Read(ctx)
	for {
		var row []bigquery.Value
		err := it.Next(&row)
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Fprintln(w, row)
	}
	return nil
}

Java

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Java の設定手順を実施してください。詳細については、BigQuery Java API のリファレンス ドキュメントをご覧ください。

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.TableResult;

// Sample to query on destination table with encryption key
public class QueryDestinationTableCMEK {

  public static void runQueryDestinationTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KMS_KEY_NAME";
    String query =
        String.format("SELECT stringField, booleanField FROM %s.%s", datasetName, tableName);
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    queryDestinationTableCMEK(query, encryption);
  }

  public static void queryDestinationTableCMEK(String query, EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      QueryJobConfiguration config =
          QueryJobConfiguration.newBuilder(query)
              // Set the encryption key to use for the destination.
              .setDestinationEncryptionConfiguration(encryption)
              .build();

      TableResult results = bigquery.query(config);

      results
          .iterateAll()
          .forEach(row -> row.forEach(val -> System.out.printf("%s,", val.toString())));
      System.out.println("Query performed successfully with encryption key.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Query not performed \n" + e.toString());
    }
  }
}

Python

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the destination table.
# table_id = "your-project.your_dataset.your_table_name"

# Set the encryption key to use for the destination.
# TODO(developer): Replace this key with a key you have created in KMS.
# kms_key_name = "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format(
#     your-project, location, your-ring, your-key
# )

job_config = bigquery.QueryJobConfig(
    destination=table_id,
    destination_encryption_configuration=bigquery.EncryptionConfiguration(
        kms_key_name=kms_key_name
    ),
)

# Start the query, passing in the extra configuration.
query_job = client.query(
    "SELECT 17 AS my_col;", job_config=job_config
)  # Make an API request.
query_job.result()  # Wait for the job to complete.

table = client.get_table(table_id)  # Make an API request.
if table.encryption_configuration.kms_key_name == kms_key_name:
    print("The destination table is written using the encryption configuration")

Cloud KMS で保護されているテーブルを読み込む

Cloud KMS で保護されているテーブルを読み込むには:

Console

読み込みジョブの抽出先テーブルを顧客管理の暗号鍵で保護するには、テーブルの読み込み時に鍵を指定します。

  1. Cloud Console で [BigQuery] ページを開きます。

    [BigQuery] ページに移動

  2. ナビゲーション パネルの [リソース] セクションでプロジェクトを展開し、データセットを選択します。

  3. ウィンドウの右側の詳細パネルで、[テーブルを作成] をクリックします。

  4. テーブルの読み込みに使用するオプションを入力します。[テーブルを作成] をクリックする前に [詳細オプション] をクリックします。

  5. [暗号化] で、[顧客管理の暗号鍵] を選択します。

  6. [顧客管理の暗号鍵を選択] プルダウン リストをクリックして、使用する鍵を選択します。使用可能な鍵が表示されない場合は、鍵のリソース ID を入力します。

    詳細オプション

  7. [テーブルを作成] をクリックします。

bq

読み込みジョブの宛先テーブルを顧客管理の暗号鍵で保護するには、--destination_kms_key フラグを設定します。

bq --location=LOCATION load \
--autodetect \
--source_format=FORMAT \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
DATASET.TABLE \
path_to_source
例:
bq load \
--autodetect \
--source_format=NEWLINE_DELIMITED_JSON \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
test2.table4 \
gs://cloud-samples-data/bigquery/us-states/us-states.json

Go

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// importJSONWithCMEK demonstrates loading newline-delimited JSON from Cloud Storage,
// and protecting the data with a customer-managed encryption key.
func importJSONWithCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	gcsRef := bigquery.NewGCSReference("gs://cloud-samples-data/bigquery/us-states/us-states.json")
	gcsRef.SourceFormat = bigquery.JSON
	gcsRef.AutoDetect = true
	loader := client.Dataset(datasetID).Table(tableID).LoaderFrom(gcsRef)
	loader.WriteDisposition = bigquery.WriteEmpty
	loader.DestinationEncryptionConfig = &bigquery.EncryptionConfig{
		// TODO: Replace this key with a key you have created in KMS.
		KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/test",
	}

	job, err := loader.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}

	if status.Err() != nil {
		return fmt.Errorf("job completed with error: %v", status.Err())
	}

	return nil
}

Java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.FormatOptions;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.LoadJobConfiguration;
import com.google.cloud.bigquery.TableId;

// Sample to load JSON data with configuration key from Cloud Storage into a new BigQuery table
public class LoadJsonFromGCSCMEK {

  public static void runLoadJsonFromGCSCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KMS_KEY_NAME";
    String sourceUri = "gs://cloud-samples-data/bigquery/us-states/us-states.json";
    // i.e. projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{cryptoKey}
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    loadJsonFromGCSCMEK(datasetName, tableName, sourceUri, encryption);
  }

  public static void loadJsonFromGCSCMEK(
      String datasetName, String tableName, String sourceUri, EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);
      LoadJobConfiguration loadConfig =
          LoadJobConfiguration.newBuilder(tableId, sourceUri)
              // Set the encryption key to use for the destination.
              .setDestinationEncryptionConfiguration(encryption)
              .setFormatOptions(FormatOptions.json())
              .setAutodetect(true)
              .build();

      // Load data from a GCS JSON file into the table
      Job job = bigquery.create(JobInfo.of(loadConfig));
      // Blocks until this load table job completes its execution, either failing or succeeding.
      job = job.waitFor();
      if (job.isDone()) {
        System.out.println("Table loaded succesfully from GCS with configuration key");
      } else {
        System.out.println(
            "BigQuery was unable to load into the table due to an error:"
                + job.getStatus().getError());
      }
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Column not added during load append \n" + e.toString());
    }
  }
}

Python

読み込みジョブの抽出先テーブルを顧客管理の暗号鍵で保護するには、LoadJobConfig.destination_encryption_configuration プロパティを EncryptionConfiguration に設定してからテーブルを読み込みます。

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the table to create.
# table_id = "your-project.your_dataset.your_table_name

# Set the encryption key to use for the destination.
# TODO: Replace this key with a key you have created in KMS.
# kms_key_name = "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format(
#     "cloud-samples-tests", "us", "test", "test"
# )

job_config = bigquery.LoadJobConfig(
    autodetect=True,
    source_format=bigquery.SourceFormat.NEWLINE_DELIMITED_JSON,
    destination_encryption_configuration=bigquery.EncryptionConfiguration(
        kms_key_name=kms_key_name
    ),
)

uri = "gs://cloud-samples-data/bigquery/us-states/us-states.json"

load_job = client.load_table_from_uri(
    uri,
    table_id,
    location="US",  # Must match the destination dataset location.
    job_config=job_config,
)  # Make an API request.

assert load_job.job_type == "load"

load_job.result()  # Waits for the job to complete.

assert load_job.state == "DONE"
table = client.get_table(table_id)

if table.encryption_configuration.kms_key_name == kms_key_name:
    print("A table loaded with encryption configuration key")

Cloud KMS で保護されているテーブルにストリーミングする

追加のパラメータを指定しなくても、CMEK で保護されている BigQuery テーブルにデータをストリーミングできます。このデータは、最終ロケーションだけでなくバッファ内でも Cloud KMS 鍵を使用して暗号化されます。CMEK テーブルでストリーミングを使用する前に、鍵の使用可能性とアクセス可能性に関する要件を確認してください。

ストリーミングの詳細については、BigQuery へのデータのストリーミングをご覧ください。

テーブルをデフォルトの暗号化から Cloud KMS 保護に変更する

bq

デフォルトの暗号化で保護されているテーブルを、Cloud KMS で保護された新しいテーブルや、元のテーブルにコピーするには、--destination_kms_key フラグを指定した bq cp コマンドを使用します。--destination_kms_key フラグによって、宛先テーブルで使用する鍵のリソース ID を指定します。

デフォルトの暗号化が使用されているテーブルを Cloud KMS で保護された新しいテーブルにコピーするには、次のコマンドを実行します。

bq cp \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
sourceDataset.sourceTableId destinationDataset.destinationTableId

デフォルトの暗号化が使用されているテーブルを Cloud KMS で保護して同じテーブルにコピーする場合は、次のコマンドを実行します。

bq cp -f \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
sourceDataset.sourceTableId sourceDataset.sourceTableId

テーブルを Cloud KMS 保護からデフォルトの暗号化に変更する場合は、--destination_kms_key フラグを使用せずに bq cp を実行して、ファイルをそれ自体にコピーします。

bq コマンドライン ツールの詳細については、bq コマンドライン ツールの使用をご覧ください。

Go

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// copyTableWithCMEK demonstrates creating a copy of a table and ensuring the copied data is
// protected with a customer managed encryption key.
func copyTableWithCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	srcTable := client.DatasetInProject("bigquery-public-data", "samples").Table("shakespeare")
	copier := client.Dataset(datasetID).Table(tableID).CopierFrom(srcTable)
	copier.DestinationEncryptionConfig = &bigquery.EncryptionConfig{
		// TODO: Replace this key with a key you have created in Cloud KMS.
		KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/test",
	}
	job, err := copier.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}
	if err := status.Err(); err != nil {
		return err
	}
	return nil
}

Java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.CopyJobConfiguration;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.TableId;

// Sample to copy a cmek table
public class CopyTableCMEK {

  public static void runCopyTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String destinationDatasetName = "MY_DESTINATION_DATASET_NAME";
    String destinationTableId = "MY_DESTINATION_TABLE_NAME";
    String sourceDatasetName = "MY_SOURCE_DATASET_NAME";
    String sourceTableId = "MY_SOURCE_TABLE_NAME";
    String kmsKeyName = "MY_KMS_KEY_NAME";
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    copyTableCMEK(
        sourceDatasetName, sourceTableId, destinationDatasetName, destinationTableId, encryption);
  }

  public static void copyTableCMEK(
      String sourceDatasetName,
      String sourceTableId,
      String destinationDatasetName,
      String destinationTableId,
      EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId sourceTable = TableId.of(sourceDatasetName, sourceTableId);
      TableId destinationTable = TableId.of(destinationDatasetName, destinationTableId);

      // For more information on CopyJobConfiguration see:
      // https://googleapis.dev/java/google-cloud-clients/latest/com/google/cloud/bigquery/JobConfiguration.html
      CopyJobConfiguration configuration =
          CopyJobConfiguration.newBuilder(destinationTable, sourceTable)
              .setDestinationEncryptionConfiguration(encryption)
              .build();

      // For more information on Job see:
      // https://googleapis.dev/java/google-cloud-clients/latest/index.html?com/google/cloud/bigquery/package-summary.html
      Job job = bigquery.create(JobInfo.of(configuration));

      // Blocks until this job completes its execution, either failing or succeeding.
      Job completedJob = job.waitFor();
      if (completedJob == null) {
        System.out.println("Job not executed since it no longer exists.");
        return;
      } else if (completedJob.getStatus().getError() != null) {
        System.out.println(
            "BigQuery was unable to copy table due to an error: \n" + job.getStatus().getError());
        return;
      }
      System.out.println("Table cmek copied successfully.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Table cmek copying job was interrupted. \n" + e.toString());
    }
  }
}

Python

このサンプルを試す前に、BigQuery クイックスタート: クライアント ライブラリの使用の Python の手順に従って設定を行ってください。詳細については、BigQuery Python API のリファレンス ドキュメントをご覧ください。

テーブルのコピー先を顧客管理の暗号鍵で保護するには、QueryJobConfig.destination_encryption_configuration プロパティの値を EncryptionConfiguration に設定してからテーブルをコピーします。

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set dest_table_id to the ID of the destination table.
# dest_table_id = "your-project.your_dataset.your_table_name"

# TODO(developer): Set orig_table_id to the ID of the original table.
# orig_table_id = "your-project.your_dataset.your_table_name"

# Set the encryption key to use for the destination.
# TODO(developer): Replace this key with a key you have created in KMS.
# kms_key_name = "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format(
#     your-project, location, your-ring, your-key
# )

job_config = bigquery.CopyJobConfig(
    destination_encryption_configuration=bigquery.EncryptionConfiguration(
        kms_key_name=kms_key_name
    )
)
job = client.copy_table(orig_table_id, dest_table_id, job_config=job_config)
job.result()  # Wait for the job to complete.

dest_table = client.get_table(dest_table_id)  # Make an API request.
if dest_table.encryption_configuration.kms_key_name == kms_key_name:
    print("A copy of the table created")

テーブルが Cloud KMS によって保護されているかどうかを判別する

  1. Cloud Console で、データセットの左側にある青色の矢印をクリックして展開するか、データセット名をダブルクリックします。これにより、データセット内のテーブルとビューが表示されます。

  2. テーブル名をクリックします。

  3. [Details] をクリックします。[Table Details] ページに、テーブルの説明とテーブル情報が表示されます。

  4. テーブルが Cloud KMS によって保護されている場合は、[Customer-Managed Encryption Key] フィールドに鍵のリソース ID が表示されます。

    保護されたテーブル

BigQuery テーブルの Cloud KMS 鍵を変更する

CMEK で保護された既存テーブルの Cloud KMS 鍵を変更するには、ALTER TABLE クエリを実行するか、API または bq コマンドライン ツールを使用できます。API と bq コマンドライン ツールを使用して Cloud KMS 鍵を変更するには、updatecp を使用します。update を使用すると、KMS で保護されているテーブルで使用されている Cloud KMS 鍵を変更できます。cp を使用する場合は、CMEK で保護されているテーブルで使用されている Cloud KMS 鍵を変更できます。また、テーブルのデフォルトの暗号化を CMEK 保護に変更することも、CMEK 保護をデフォルトの暗号化に戻すこともできます。update のメリットは、cp より高速であること、テーブル デコレータの使用が可能であることです。

Console

  1. Cloud Console で [BigQuery] ページを開きます。

    [BigQuery] ページに移動

  2. [クエリを新規作成] をクリックします。

  3. [New Query] テキスト領域に DDL ステートメントを入力します。kms_key_name 値に、テーブルの保護に使用する鍵のリソース ID を指定します。

    #standardSQL
    ALTER TABLE mydataset.mytable
    SET OPTIONS (
    kms_key_name="projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY"
    )
    
  4. [実行] をクリックします。

bq

Cloud KMS で保護されているテーブルの鍵を変更するには、--destination_kms_key フラグを指定して bq cp コマンドを使用します。--destination_kms_key フラグで、テーブルで使用する鍵のリソース ID を指定します。

bq update \
--destination_kms_key projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
-t DATASET_ID.TABLE_ID

Go

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// updateTableChangeCMEK demonstrates how to change the customer managed encryption key that protects a table.
func updateTableChangeCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydatasetid"
	// tableID := "mytableid"
	ctx := context.Background()

	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	tableRef := client.Dataset(datasetID).Table(tableID)
	meta, err := tableRef.Metadata(ctx)
	if err != nil {
		return err
	}
	update := bigquery.TableMetadataToUpdate{
		EncryptionConfig: &bigquery.EncryptionConfig{
			// TODO: Replace this key with a key you have created in Cloud KMS.
			KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/otherkey",
		},
	}
	if _, err := tableRef.Update(ctx, update, meta.ETag); err != nil {
		return err
	}
	return nil
}

Java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.Table;
import com.google.cloud.bigquery.TableId;

// Sample to update a cmek table
public class UpdateTableCMEK {

  public static void runUpdateTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KEY_NAME";
    // Set a new encryption key to use for the destination.
    // i.e. projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{cryptoKey}
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    updateTableCMEK(datasetName, tableName, encryption);
  }

  public static void updateTableCMEK(
      String datasetName, String tableName, EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      Table table = bigquery.getTable(TableId.of(datasetName, tableName));
      bigquery.update(table.toBuilder().setEncryptionConfiguration(encryption).build());
      System.out.println("Table cmek updated successfully");
    } catch (BigQueryException e) {
      System.out.println("Table cmek was not updated. \n" + e.toString());
    }
  }
}

Python

テーブルの顧客管理の暗号鍵を変更するには、Table.encryption_configuration プロパティを新しい EncryptionConfiguration オブジェクトに変更してからテーブルを更新します。

# from google.cloud import bigquery
# client = bigquery.Client()

assert table.encryption_configuration.kms_key_name == original_kms_key_name

# Set a new encryption key to use for the destination.
# TODO: Replace this key with a key you have created in KMS.
updated_kms_key_name = (
    "projects/cloud-samples-tests/locations/us/keyRings/test/cryptoKeys/otherkey"
)
table.encryption_configuration = bigquery.EncryptionConfiguration(
    kms_key_name=updated_kms_key_name
)

table = client.update_table(table, ["encryption_configuration"])  # API request

assert table.encryption_configuration.kms_key_name == updated_kms_key_name
assert original_kms_key_name != updated_kms_key_name

データセットのデフォルト鍵を設定する

データセット全体のデフォルト Cloud KMS 鍵を設定できます。この鍵は、テーブルの作成時に別の Cloud KMS 鍵を指定しない限り、データセットに新しく作成されたすべてのテーブルに適用されます。デフォルト鍵は、既存のテーブルには適用されません。デフォルト鍵を変更しても、既存のテーブルは変更されません。この鍵は、変更後に作成された新しいテーブルにのみ適用されます。

データセットのデフォルト鍵は、次の方法で適用、変更または削除できます。

Cloud KMS 鍵への BigQuery のアクセス権を削除する

Cloud KMS 鍵への BigQuery のアクセス権は、その鍵に対する IAM 権限を取り消すことによって、いつでも削除できます。

BigQuery が Cloud KMS 鍵へのアクセス権を失うと、ユーザー エクスペリエンスが大幅に低下し、データが失われる可能性があります。

  • その CMEK で保護されているテーブルのデータにアクセスできなくなるため、querycpextracttabledata.list はすべて失敗します。

  • その CMEK で保護されているテーブルに新しいデータを追加できません。

  • アクセス権を付与し直した後も、数日間は、そのテーブルへのクエリのパフォーマンスが低下する可能性があります。

Cloud KMS 鍵のローテーションによる影響

テーブルに関連付けられている Cloud KMS 鍵のローテーションが行われても、BigQuery がそのテーブルの暗号鍵のローテーションを自動的に行うことはありません。既存のテーブルでは引き続き、作成時に使用された鍵バージョンが使用されます。新しいテーブルでは現在の鍵バージョンが使用されます。

制限事項

Cloud KMS 鍵への BigQuery のアクセス

Cloud KMS 鍵は、次の場合に BigQuery で使用でき、アクセス可能とみなされます。

  • その鍵が有効になっている
  • BigQuery サービス アカウントに、その鍵に対する暗号化および復号の権限がある

以下のセクションでは、鍵にアクセスできない場合のストリーミング挿入と長期間アクセスできないデータへの影響について説明します。

ストリーミング挿入への影響

Cloud KMS 鍵は、ストリーミング挿入リクエスト後の 48 時間で 24 時間以上連続して利用可能かつアクセス可能である必要があります。鍵が利用できず、アクセスもできない場合、ストリーミング データは完全には保持されず、失われる可能性があります。ストリーミング挿入の詳細については、BigQuery へのデータのストリーミングをご覧ください。

長期間アクセスできないデータへの影響

BigQuery はマネージド ストレージを提供するため、長期間アクセスできないデータは BigQuery のアーキテクチャと互換性がありません。特定の BigQuery テーブルの Cloud KMS 鍵が 60 日間連続して利用不可かつアクセス不可である場合、BigQuery はそのテーブルと関連データを削除することを選択することがあります。データが削除される少なくとも 7 日前までに、BigQuery は請求先アカウントに関連付けられたメールアドレスにメールを送信します。

テーブル デコレータの使用

Cloud KMS によって保護されているテーブルのデータが、loadcpquery のいずれかのオペレーションの書き込み処理 WRITE_TRUNCATE を使用して置き換えられると、スナップショット デコレータの時刻によっては、テーブル デコレータを使用するクエリからテーブルへのアクセスができなくなります。

テーブルが時刻 T に置き換えられて、スナップショット デコレータ snapshot_timeT より早い時刻であるとした場合に、snapshot_time をクエリできるかどうかを以下の表に示します。

T 以前の暗号化の種類 T 以後の暗号化の種類 snapshot_time
Cloud KMS 暗号化 Cloud KMS 暗号化 クエリ不可
デフォルトの暗号化 Cloud KMS 暗号化 クエリ可能
Cloud KMS 暗号化 デフォルトの暗号化 クエリ不可

範囲デコレータの使用時も、同様のロジックが <time2> に適用されるため、注意が必要です。

よくある質問

Cloud KMS 鍵への権限が必要なのは誰ですか?

顧客管理の暗号鍵を使用している場合、権限を繰り返し指定する必要はありません。Cloud KMS 鍵を使用して暗号化と復号を行う権限が BigQuery サービス アカウントにある限り、BigQuery テーブルへのアクセス権限を持つユーザーは、Cloud KMS 鍵への直接アクセス権がないユーザーであっても、データにアクセスできます。

どのサービス アカウントが使用されますか?

テーブルの Cloud プロジェクトに関連付けられる BigQuery サービス アカウントが、テーブルのデータを復号するために使用されます。BigQuery サービス アカウントはプロジェクトごとに一意です。Cloud KMS で保護されている匿名テーブルにデータを書き込むジョブでは、そのジョブのプロジェクトのサービス アカウントが使用されます。

たとえば、table1table2table3 という CMEK で保護されている 3 つのテーブルについて考えてみます。宛先テーブルを {project3.table3} として {project1.table1, project2.table2} からデータをクエリする場合は、次のようになります。

  • project1.table1 に対しては project1 のサービス アカウントが使用される
  • project2.table2 に対しては project2 のサービス アカウントが使用される
  • project3.table3 に対しては project3 のサービス アカウントが使用される

BigQuery では、どのように Cloud KMS 鍵を使用できますか?

BigQuery は Cloud KMS 鍵を使用して、ユーザークエリ(tabledata.listjobs.insert など)に応じてデータを復号します。

BigQuery では、データを読み取りに最適化された形式に変換するなど、データのメンテナンスやストレージ最適化タスクにもこの鍵を使用できます。

どの暗号ライブラリが使用されていますか?

BigQuery は CMEK 機能を Cloud KMS に依存しています。 Cloud KMS は Tink を暗号化に使用します。

追加のサポートを得る方法

不明な点がある場合は、BigQuery のサポートまたは Cloud KMS のサポートをご覧ください。

エラーのトラブルシューティング

よくあるエラーと推奨される軽減策について以下に説明します。

エラー 推奨
Please grant Cloud KMS CryptoKey Encrypter/Decrypter role プロジェクトに関連付けられる BigQuery サービス アカウントに、指定した Cloud KMS 鍵を操作するのに十分な IAM 権限がありません。エラー メッセージ内またはこのドキュメントの手順に従って、適切な IAM 権限を付与してください。
Existing table encryption settings do not match encryption settings specified in the request このエラーは、宛先テーブルの暗号化設定がリクエストの暗号化設定と一致しない場合に発生することがあります。軽減策として、書き込み処理 TRUNCATE を使用してテーブルを置き換えるか、別の宛先テーブルを指定します。
This region is not supported Cloud KMS 鍵のリージョンが、宛先テーブルの BigQuery データセットのリージョンと一致していません。軽減策として、データセットに一致するリージョンにある鍵を選択するか、鍵リージョンに一致するデータセットに読み込みます。