使用 Cloud KMS 進行資料欄層級加密

您可以使用 Cloud Key Management Service (Cloud KMS) 加密金鑰,進而加密 BigQuery 資料表中的值。您可以搭配使用 AEAD 加密函式和 Cloud KMS 鍵組包裝鍵組,在資料欄層級提供第二層防護機制。

簡介

為提供額外保護,Cloud KMS 會使用第二組金鑰加密金鑰 (KEK) 來加密資料加密金鑰 (DEK)。在 BigQuery 中,參照加密金鑰組而非明文金鑰組,有助於降低金鑰外洩的風險。KEK 是對稱加密金鑰組,會安全地儲存在 Cloud KMS 中,並使用 Identity and Access Management (IAM) 角色和權限進行管理。

BigQuery 支援確定性和非確定性加密函式。在確定性加密中,如果儲存的資料片段和其他已驗證資料 (選用) 都相同,則密文也會相同。這可支援以加密資料欄為基礎的匯總和彙整作業。使用非確定性加密時,無論加密資料為何,儲存的密文都是唯一的,因此無法進行叢集、匯總和彙整作業。

在查詢執行期間,您會提供 KEK 的 Cloud KMS 資源路徑,以及來自包裝 DEK 的密文。BigQuery 會呼叫 Cloud KMS 來解除 DEK 的包裝,然後使用該金鑰解密查詢中的資料。未包裝的 DEK 版本只會在查詢期間儲存在記憶體中,然後遭到銷毀。

如果您在支援 Cloud External Key Manager區域中使用 Cloud KMS,就可以在 Cloud KMS 中使用 Cloud EKM 的金鑰。

用途

使用 Cloud KMS 金鑰加密的用途包括:

  • 需要儲存在 BigQuery 中,但不需以明文形式儲存金鑰集的外部加密資料。接著,您可以從資料表匯出資料,或使用 SQL 查詢解密資料。
  • 在 BigQuery 中對加密資料實施「雙重存取權控管」機制。使用者必須同時擁有資料表和加密金鑰的權限,才能以明文讀取資料。
使用者權限矩陣
資料表權限 沒有資料表權限
金鑰的權限 讀取及解密已加密的資料。 沒有存取權。
金鑰上沒有權限 讀取加密資料。 沒有存取權。

如果使用者有權存取 KMS 金鑰,且可存取包裝的金鑰集,SQL 函式就能解開金鑰集並解密密文。使用者也可以使用 Cloud KMS REST APICLI 解除包裝金鑰組。
以下查詢範例使用 KMS SQL 函式解密非決定論的密文:

SELECT
  AEAD.DECRYPT_STRING(
    KEYS.KEYSET_CHAIN(@kms_resource_name, @first_level_keyset),
    ciphertext,
    additional_authenticated_data)
FROM
  ciphertext_table
WHERE
  ...

用途範例

假設在某個實作中,郵遞區號被視為機密資訊。您可以使用 AEAD 加密函式將郵遞區號資料插入 BigQuery 資料表,藉此加密 Zipcode 欄。在本範例中,我們會將 AEAD.ENCRYPT 函式與包裝的金鑰組管理函式搭配使用。KEYS.KEYSET_CHAIN 函式會使用 KEK 解密數位加密金鑰,而 AEAD.ENCRYPT 函式會將資訊傳遞至 KMS。

用於加密和解密的金鑰組鏈結可確保資料加密金鑰 (DEK) 會使用 KEK 加密或包裝,並透過該 KEK 傳遞。封裝的 DEK 會在 SQL 函式中解密或解除包裝,然後用於加密或解密資料。

AEAD 非確定性函式可在資料存取時,透過在資料表上執行的查詢中的函式解密資料。

圖片

AEAD 決定性函式可在資料表上執行查詢時使用函式,並支援使用加密資料進行匯總和彙整,藉此在存取資料時解密資料。

圖片

非確定性函式語法

使用非確定性函式的支援語法包括:

AEAD.ENCRYPT(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  plaintext,
  additional_authenticated_data)
AEAD.DECRYPT_STRING(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_authenticated_data)
AEAD.DECRYPT_BYTES(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_authenticated_data)

請參閱 AEAD.DECRYPT_BYTESAEAD.ENCRYPTAEAD.DECRYPT_STRINGKEYS.KEYSET_CHAIN 函式語法。

決定性函式語法

使用確定性函式的支援語法包括:

DETERMINISTIC_ENCRYPT(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  plaintext,
  additional_data)
DETERMINISTIC_DECRYPT_STRING(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_data)
DETERMINISTIC_DECRYPT_BYTES(
  KEYS.KEYSET_CHAIN(kms_resource_name, first_level_keyset),
  ciphertext,
  additional_data)

請參閱 DETERMINISTIC_DECRYPT_BYTESDETERMINISTIC_ENCRYPTDETERMINISTIC_DECRYPT_STRINGKEYS.KEYSET_CHAIN 函式語法。

角色和權限

如要查看 Cloud KMS 的角色清單,請參閱「Cloud KMS 權限和角色」。

限制

使用 Cloud KMS 加密時,有下列限制:

  • Cloud KMS 金鑰必須位於與查詢相同的區域或多區域。基於可靠性考量,我們不允許使用全球 Cloud KMS 金鑰。

  • 您無法使用 KEYS.ROTATE_KEYSET 函式旋轉已包裝的鍵組。

  • 使用者可在 診斷查詢計畫中查看 BigQuery 查詢中的常數參數。這項因素可能會影響 KEYSET_CHAIN 函式的 kms_resource_namefirst_level_keyset 參數。金鑰絕不會以明文形式公開,而且必須具備 Cloud KMS 金鑰的權限,才能解密包裝的金鑰組合。這個方法可確保金鑰不會透過診斷查詢計畫公開,除非使用者具有解密金鑰集合的權限。

  • 資料欄層級加密功能與類型為基礎的安全性分類搭配使用時,有下列限制:

    • 資料欄層級安全性:使用者只能解密或加密自己有權存取的資料欄資料。

    • 資料列層級安全性:使用者只能解密自己有權存取的資料列資料。

  • 與原始加密函式 (以明文傳送金鑰資料) 的效能相比,資料欄層級 SQL 函式對效能並無重大影響。

事前準備

如要使用 Cloud KMS 金鑰、金鑰組合、加密表格、確定性和非確定性函式,您必須先完成下列步驟 (如果尚未完成):

  1. 建立 Google Cloud 專案

  2. 建立 BigQuery 資料集

  3. 建立 Cloud KMS 金鑰環

  4. 為使用軟體或硬體安全性模組 (HSM) 防護等級的加密資料欄建立 Cloud KMS 金鑰

  5. 授予使用者權限,以便使用 Cloud KMS 金鑰、加密和解密功能

請注意下列概念,因為這些概念會在後續章節中提及:

  • PROJECT_ID:Google Cloud 專案名稱。

  • DATASET_NAME:BigQuery 資料集名稱。

  • LOCATION_ID:BigQuery 資料集的位置。

  • TABLE_NAME:BigQuery 資料表的名稱。

  • KEY_RING_ID:Cloud KMS 金鑰環的名稱。

  • KEY_ID:Cloud KMS 金鑰的名稱。

  • KMS_KEY:Cloud KMS 金鑰 (KEK),格式如下:

    'gcp-kms://projects/PROJECT_ID/locations/LOCATION_ID/keyRings/KEY_RING_ID/cryptoKeys/KEY_ID'

    以下是 Cloud KMS 金鑰的範例:

    'gcp-kms://projects/myProject/locations/us/keyRings/myKeyRing/cryptoKeys/myKeyName'
    
  • KMS_KEY_SHORT:類似 KMS_KEY,但格式如下:

    projects/PROJECT_ID/locations/LOCATION_ID/keyRings/KEY_RING_ID/cryptoKeys/KEY_ID
  • KEYSET_DECODED:解碼的鍵組,做為 BYTES 序列。輸出結果看起來與已解碼的包裝金鑰組相似。

    雖然鍵值集函式會以位元組形式傳回鍵值集,但使用者輸出內容會以編碼字串顯示。如要將已編碼的金鑰組轉換為已解碼的金鑰組,請參閱「解碼 Cloud KMS 金鑰組

  • KEYSET_ENCODED:編碼的鍵組,做為 STRING。輸出結果看起來與經過編碼的包裝金鑰集相似。

    如要將已編碼的金鑰組轉換為已解碼的金鑰組,請參閱「解碼 Cloud KMS 金鑰組

  • WRAPPED_KEYSET_DECODED:已解碼的包裝金鑰組,做為 BYTES 序列。以下是輸出結果的範例:

    b'\x0a$\x00\xa6\xee\x12Y\x8d|l"\xf7\xfa\xc6\xeafM\xdeefy\xe9\x7f\xf2z\xb3M\
    xf6"\xd0\xe0Le\xa8\x8e\x0fR\xed\x12\xb7\x01\x00\xf0\xa80\xbd\xc1\x07Z\\
    \xd0L<\x80A0\x9ae\xfd(9\x1e\xfa\xc8\x93\xc7\xe8\...'
    

    雖然包裝的鍵組函式會以位元組形式傳回包裝的鍵組,但使用者輸出內容會以編碼字串顯示。如要將已編碼的包裝金鑰組轉換為已解碼的包裝金鑰組,請參閱「解碼 Cloud KMS 金鑰組

  • WRAPPED_KEYSET_ENCODED:已編碼的包裝金鑰組,做為 STRING。以下是輸出結果的範例:

    'CiQApu4SWTozQ7lNwITxpEvGlo5sT2rv1tyuSv3UAMtoTq/lhDwStwEA8KgwvX7CpVVzhWWMkRw
    WZNr3pf8uBIlzHeunCy8ZsQ6CofQYFpiBRBB6k/QqATbiFV+3opnDk/6dBL/S8OO1WoDC+DdD9
    uzEFwqt5D20lTXCkGWFv1...'
    

    如要將已編碼的包裝金鑰組轉換為已解碼的包裝金鑰組,請參閱「解碼 Cloud KMS 金鑰組

金鑰管理

以下各節說明您可以使用 Cloud KMS 金鑰執行的常見工作。

建立金鑰組

您可以建立已包裝的鍵組或原始鍵組。如要這樣做,請完成下列各節中的步驟。

建立原始金鑰組

執行下列查詢,建立鍵組,其中鍵的類型為 DETERMINISTIC_AEAD_AES_SIV_CMAC_256

SELECT KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') AS raw_keyset

建立已包裝金鑰組

執行下列查詢,建立 Cloud KMS 包裝的金鑰組合,其中的金鑰類型為 DETERMINISTIC_AEAD_AES_SIV_CMAC_256

SELECT KEYS.NEW_WRAPPED_KEYSET(
  KMS_KEY,
  'DETERMINISTIC_AEAD_AES_SIV_CMAC_256')

解碼金鑰組

雖然傳回鍵組的 SQL 函式會以 BYTES 格式產生鍵組,但使用者顯示的結果會以 STRING 格式編碼及顯示。如果您想將這個已編碼的字串轉換為可用於字面金鑰加密函式的解碼位元組序列,請使用下列查詢。

解碼已包裝的金鑰組

執行以下查詢,對 Cloud KMS 包裝金鑰組進行解碼。

SELECT FORMAT('%T', FROM_BASE64(WRAPPED_KEYSET_ENCODED'))

解碼原始金鑰組

執行下列查詢,解碼原始鍵組。

SELECT FORMAT('%T', FROM_BASE64(KEYSET_ENCODED'))

重新包裝已包裝的鍵組

執行下列查詢,使用新的 Cloud KMS 金鑰重新包裝 Cloud KMS 已包裝的金鑰組合。KMS_KEY_CURRENT 代表用於加密金鑰組的新 KMS_KEYKMS_KEY_NEW 代表用於加密金鑰組的新 KMS_KEY

SELECT KEYS.REWRAP_KEYSET(
  KMS_KEY_CURRENT,
  KMS_KEY_NEW,
  WRAPPED_KEYSET_DECODED)

輪替已包裝金鑰組

執行以下查詢,以 DETERMINISTIC_AEAD_AES_SIV_CMAC_256 類型的金鑰輪替 Cloud KMS 包裝金鑰組。

SELECT KEYS.ROTATE_WRAPPED_KEYSET(
  KMS_KEY,
  WRAPPED_KEYSET_DECODED,
  'DETERMINISTIC_AEAD_AES_SIV_CMAC_256')

從已包裝的鍵組產生原始鍵組

部分加密函式需要原始金鑰組。如要解密 Cloud KMS 包裝金鑰組,並產生原始金鑰組,請完成下列步驟。

  1. 建立已包裝的金鑰組

  2. 在 bq 指令列工具中輸入下列指令,將包裝的鍵組儲存在名為 keyset_to_unwrap 的檔案中,解密包裝的鍵組,並以 KEYSET_DECODED 格式產生輸出內容:

    echo WRAPPED_KEYSET_ENCODED | base64 -d > /tmp/decoded_wrapped_key
    gcloud kms decrypt \
    --ciphertext-file=/tmp/decoded_wrapped_key \
    --key=KMS_KEY_SHORT \
    --plaintext-file=/tmp/keyset_to_unwrap.dec \
    --project=PROJECT_ID
    od -An --format=o1 /tmp/keyset_to_unwrap.dec | tr ' ' '\'

從原始鍵組產生已包裝的鍵組

部分加密函式需要 Cloud KMS 包裝的金鑰組。如要加密原始金鑰組以產生包裝金鑰組,請完成下列步驟。

  1. 建立原始金鑰組

  2. 在 bq 指令列工具中輸入下列指令,即可將原始鍵組儲存在名為 keyset_to_wrap 的檔案中、將原始鍵組加密,並以 WRAPPED_KEYSET_DECODED 格式產生輸出內容:

    echo KEYSET_ENCODED | base64 -d > /tmp/decoded_key
    gcloud kms encrypt \
    --plaintext-file=/tmp/decoded_key \
    --key=KMS_KEY_SHORT \
    --ciphertext-file=/tmp/keyset_to_wrap.dec \
    --project=PROJECT_ID
    od -An --format=o1 /tmp/keyset_to_wrap.dec | tr ' ' '\'

為資料遺失防護函式產生已包裝金鑰

如要使用DLP 函式,您需要加密編譯金鑰,然後使用該金鑰取得包裝金鑰。

  1. 如要產生新的加密金鑰,請在指令列中執行下列指令。索引鍵大小可以是 16、24 或 32 位元組。以下範例使用 16 個位元組的金鑰:

    openssl rand 16 > rand.key.16.bin
    
  2. 使用 KMS 金鑰包裝產生的 16 個位元組金鑰。請參閱以下範例:

    KEYRING=projects/myproject/locations/us/keyRings/kms-test
    KEY=projects/myproject/locations/us/keyRings/kms-test/cryptoKeys/test-Kek
    PROJECT="myproject"
    
    gcloud kms encrypt --project $PROJECT --location us --keyring $KEYRING --key $KEY --plaintext-file ./rand.key.16.bin --ciphertext-file ./rand.key.16.wrapped
    
  3. 您現在可以取得包裝金鑰的 BYTES 文字常值,或包裝金鑰的 base64 格式。

    • 位元組字面值

      username:~/tmp$ od -b ./rand.key.16.wrapped | cut -d ' ' -f 2- | head -n -1 | sed  -e 's/^/ /' | tr ' ' '\'
      

      輸出內容如下:

      \012\044\000\325\155\264\153\246\071\172\130\372\305\103\047\342\356\061\077\014\030\126\147\041\126\150\012\036\020\202\215\044\267\310\331\014\116\233\022\071\000\363\344\230\067\274\007\340\273\016\212\151\226\064\200\377\303\207\103\147\052\267\035\350\004\147\365\251\271\133\062\251\246\152\177\017\005\270\044\141\211\116\337\043\035\263\122\340\110\333\266\220\377\247\204\215\233
      
    • Base64 格式

      username:~/tmp$ base64 ./rand.key.16.wrapped
      

      輸出內容如下:

      CiQA1W20a6Y5elj6xUMn4u4xPwwYVmchVmgKHhCCjSS3yNkMTpsSOQDz5Jg3vAfguw6KaZY0gP/Dh0NnKrcd6ARn9am5WzKppmp/DwW4JGGJTt8jHbNS4EjbtpD/p4SNmw==
      

取得鍵組中的鍵數

執行以下查詢,取得原始鍵組中的鍵數。

  1. 如果您使用的是包裝的鍵組,請先產生原始鍵組

  2. 使用原始鍵組執行以下查詢:

    SELECT KEYS.KEYSET_LENGTH(KEYSET_DECODED) as key_count;

取得鍵集的 JSON 表示法

執行以下查詢,即可查看原始金鑰組的 JSON 表示法。

  1. 如果您使用的是包裝的鍵組,請先產生原始鍵組

  2. 使用原始鍵組執行以下查詢:

    SELECT KEYS.KEYSET_TO_JSON(KEYSET_DECODED);

加密與解密

您可以使用原始金鑰組或包裝金鑰組,加密資料表中的資料欄。您也可以選擇在資料欄中使用確定性或非確定性加密。本節的範例使用包裝的鍵組,但您可以將包裝的鍵組替換為原始鍵組。

使用包裝的金鑰組以決定性方式加密資料欄

執行以下查詢,建立資料表,並在名為 encrypted_content 的資料欄中,儲存使用確定性加密的 Cloud KMS 包裝金鑰組。

  1. 建立已包裝的金鑰組

  2. 使用包裝的金鑰組合加密資料欄。

    CREATE OR REPLACE TABLE DATASET_NAME.TABLE_NAME AS
      SELECT DETERMINISTIC_ENCRYPT(
        KEYS.KEYSET_CHAIN(KMS_KEY, WRAPPED_KEYSET_DECODED),
        'plaintext',
        '') AS encrypted_content

使用包裝的金鑰組以確定方式解密資料欄

執行下列查詢,使用 Cloud KMS 包裝的金鑰組,以確定性方式解密包含加密內容的資料欄。這項查詢假設您參照的資料表含有名為 encrypted_content 的資料欄。

SELECT DETERMINISTIC_DECRYPT_STRING(
  KEYS.KEYSET_CHAIN(KMS_KEY, WRAPPED_KEYSET_DECODED),
  encrypted_content,
  '')
FROM DATASET_NAME.TABLE_NAME

使用已包裝的金鑰組以非決定論方式加密資料欄

請參閱「使用包裝的金鑰組以確定性加密資料欄」,但將 DETERMINISTIC_ENCRYPT 替換為 AEAD.ENCRYPT。請確認您的鍵組為 AEAD_AES_GCM_256 類型。

使用已包裝的金鑰組,以非決定論方式解密資料欄

請參閱「使用包裝的鍵組以確定性方式解密資料欄」,但將 DETERMINISTIC_DECRYPT_STRING 替換為 AEAD.DECRYPT_STRING。請確認您的鍵組為 AEAD_AES_GCM_256 類型。

後續步驟

  • 進一步瞭解 Cloud KMS。本主題包含 Google Cloud的資料欄層級加密概念資訊。
  • 進一步瞭解 BigQuery 的 AEAD 加密功能。本主題包含資料欄層級加密的概念資訊,適用於 BigQuery。
  • 進一步瞭解 BigQuery 的 AEAD 加密函式。本主題包含可在 BigQuery 中用於資料欄層級加密的所有 SQL 函式。