使用欄位層級加密來協助保護敏感資料 - Amazon CloudFront

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用欄位層級加密來協助保護敏感資料

使用 Amazon CloudFront,您可以使用 end-to-end HTTPS 強制執行與原始伺服器的安全連線。欄位層級加密可新增額外的安全層,可讓您在整個系統處理過程中保護特定的資料,以便只有特定應用程式才能看到它。

欄位層級加密可讓您的使用者安全地將敏感資訊上傳到您的 Web 伺服器。使用者提供的敏感資訊會在邊緣、靠近使用者處加密,並在整個應用程式堆疊中保持加密。此加密可確保只有需要資料的應用程式 (並具有可解密的登入資料) 才能執行這項操作。

要使用字段級加密,請在 CloudFront 配置分發時,請在 POST 請求中指定要加密的字段集以及用於加密它們的公鑰。您可以在請求中加密多達 10 個資料欄位。(您無法使用欄位層級加密對請求中的所有資料進行加密;您必須指定要加密的各個欄位。)

當將帶有欄位層級加密的 HTTPS 請求轉發到原始伺服器,並且該請求被路由到原始應用程式或子系統中時,敏感資料仍然被加密,從而降低了敏感資料遭受洩露或意外遺失資料的風險。出於業務考量而需對敏感資料存取的元件,例如需要存取信用卡號碼的付款系統,可以使用適當的私有金鑰來解密和存取該資料。

注意

為了使用欄位層級加密,您的原始伺服器必須支援區塊編碼。

中的欄位層級加密 CloudFront

CloudFront 欄位層級加密使用非對稱加密,也稱為公開金鑰加密。您提供公開金鑰 CloudFront,而您指定的所有敏感資料都會自動加密。您提供的金鑰 CloudFront 無法用於解密加密值;只有您的私密金鑰才能執行此動作。

只將敏感資料加密

欄位層級加密概觀

以下步驟概述了設定欄位層級加密。對於特定的步驟,請參閱設定欄位層級加密

  1. 取得公有金鑰/私有金鑰對。在 CloudFront中開始設定欄位層級加密之前,您必須取得並新增公開金鑰。

  2. 建立欄位層級加密的設定檔。您在中 CloudFront建立的欄位層級加密設定檔會定義您要加密的欄位。

  3. 建立欄位層級加密的組態。組態會指定要使用的設定檔 (根據請求的內容類型或查詢引數),用來將特定資料欄位加密。您也可以針對不同案例選擇所需的請求轉發行為選項。例如,您可以設定要求 URL 中的 query 引數所指定的描述檔名稱不存在於中的行為 CloudFront。

  4. 至快取行為的連結。將組態連結至發行版的快取行為,以指定何時 CloudFront應該加密資料。

設定欄位層級加密

依照以下步驟開始使用欄位層級加密。若要了解欄位層級加密的配額 (先前稱為限制),請參閱 配額

步驟 1:建立 RSA 金鑰對

若要開始使用,您必須建立包含公有金鑰和私有金鑰的 RSA 金鑰對。公開金鑰可 CloudFront 以加密資料,而私密金鑰可讓原始元件解密已加密的欄位。您可以使用 OpenSSL 或其他工具來建立金鑰對。金鑰大小必須為 2048 個位元。

例如,如果您使用的是 OpenSSL,您可以使用以下命令來產生長度為 2048 個位元組的金鑰對,並將其儲存在檔案 private_key.pem 中:

openssl genrsa -out private_key.pem 2048

產生的檔案同時包含公有和私有金鑰。要從該檔案中擷取公有金鑰,請執行以下命令:

openssl rsa -pubout -in private_key.pem -out public_key.pem

公有金鑰檔案 (public_key.pem) 包含您在下列步驟中貼上的編碼鍵值。

步驟 2:將您的公鑰添加到 CloudFront

取得 RSA key pair 後,請將您的公開金鑰新增至 CloudFront。

若要將您的公開金鑰新增至 CloudFront (主控台)
  1. 登入 AWS Management Console 並開啟 CloudFront 主控台,位於https://console.aws.amazon.com/cloudfront/v4/home

  2. 在導覽窗格中,選擇 Public key (公有金鑰)。

  3. 選擇 Add public key (新增公有金鑰)。

  4. Key name (金鑰名稱) 中,輸入金鑰的獨特名稱。名稱不能有空格,並且只能包含英數字元、底線 (_) 和 連字號 (-)。最大字元數為 128。

  5. 針對 Key value (鍵值),貼上公有金鑰的編碼鍵值 (包括 -----BEGIN PUBLIC KEY----------END PUBLIC KEY----- 行)。

  6. 針對 Comment (註解),加入選擇性的註解。例如,您可以包含公有金鑰的到期日期。

  7. 選擇 Add (新增)。

您可以重複程序中的步驟來新增更多要與之 CloudFront 搭配使用的金鑰。

步驟 3:建立欄位層級加密的設定檔。

在您新增至少一個公開金鑰之後 CloudFront,請建立描述檔,告知要加密CloudFront 哪些欄位。

建立欄位層級加密的設定檔 (主控台)
  1. 在導覽窗格中,選擇 Field-level encryption (欄位層級加密)

  2. 選擇 Create profile (建立設定檔)

  3. 填寫下列欄位:

    設定檔名稱

    請輸入設定檔的專屬名稱。名稱不能有空格,並且只能包含英數字元、底線 (_) 和 連字號 (-)。最大字元數為 128。

    公有金鑰名稱

    在下拉式清單中,選擇您在步驟 2 CloudFront 中新增至的公開金鑰名稱。 CloudFront 使用金鑰來加密您在此設定檔中指定的欄位。

    供應商名稱

    輸入片語以協助識別您的金鑰,例如您取得到金鑰對的供應商。當應用程式解密資料欄位時,將需要此資訊以及私有金鑰。供應商名稱不能有空格,並且只能包含英數字元、冒號 (:)、底線 (_) 和 連字號 (-)。最大字元數為 128。

    欄位名稱模式符合

    輸入要加密的資料欄位名稱或識別要 CloudFront求中資料欄位名稱的模式。選擇 + 選項來新增您想要使用此金鑰加密的所有欄位。

    對於欄位名稱模式,您可以輸入資料欄位的完整名稱 DateOfBirth,例如,或僅輸入萬用字元 (*) 的名稱的第一部分,例如 CreditCard *。除了可選的萬用字元 (*) 外,欄位名稱模式必須只包含英數字元、方括號 ([和])、句號 (.)、底線 (_) 和連字號 (-)。

    請確定不要為不同的欄位名稱模式使用重疊的字元。例如,如果您有 ABC* 的欄位名稱模式,則不能新增另一個 AB* 的欄位名稱模式。此外,欄位名稱區分大小寫,可以使用的字元數上限為 128。

    註解

    (選用) 輸入有關此設定檔的評論。最多可使用 128 個字元。

  4. 填寫完欄位後,選擇 Create profile (建立設定檔)

  5. 如果您想新增更多設定檔,請選擇 Add profile (新增設定檔)

步驟 4:建立組態

建立一或多個欄位層級加密設定檔之後,請建立組態,以指定要求的內容類型,其中包括要加密的資料、用於加密的設定檔,以及其他指定您 CloudFront 要如何處理加密的選項。

例如,當 CloudFront 無法加密資料時,您可以在下列情況中指定是否 CloudFront要封鎖或轉寄要求至您的來源:

  • 當請求的內容類型不在配置中時 — 如果您尚未將內容類型添加到配置中,則可以指定是否 CloudFront 應將具有該內容類型的請求轉發到來源,而不加密數據字段,或阻止請求並返回錯誤。

    注意

    如果您將內容類型新增至設定,但尚未指定要搭配該類型使用的描述檔,請務 CloudFront 必將具有該內容類型的要求轉送至來源。

  • 當查詢引數中提供的配置文件名稱未知時 — 當您指定具有配置文件名稱的fle-profile查詢參數時,您可以指定是否 CloudFront 應該在不加密數據字段的情況下將請求發送到原點,還是阻止請求並返回錯誤。

在組態中,還可以指定一個在 URL 中做為查詢參數所提供的設定檔是否要覆寫了一個已對應到該查詢內容類型的設定檔。根據預設, CloudFront 會使用已對應至內容類型的描述檔 (如果您指定)。這可讓您擁有一個預設情況下使用的設定檔,但也會決定您所希望強制執行不同設定檔的特定請求。

因此,例如,您可以指定 (在您的組態中) SampleProfile 做為要使用的查詢參引數設定檔。然後https://d1234.cloudfront.net,您可以使用 URL https://d1234.cloudfront.net?fle-profile=SampleProfile 而不是 CloudFront 使SampleProfile用此請求,而不是為請求的內容類型設置的配置文件。

您最多可以為單一帳戶建立 10 個組態,然後將其中一個組態與的該帳戶的任何分佈的快取行為相關聯。

建立欄位層級加密的組態 (主控台)
  1. Field-level encryption (欄位層級加密) 頁面,選擇 Create configuration (建立組態)

    注意:如果您尚未建立至少一個設定檔,則不會看到用於建立組態的選項。

  2. 請填寫以下欄位指定要使用的設定檔。(有些欄位無法變更。)

    內容類型 (無法變更)

    內容類型設定為 application/x-www-form-urlencoded,無法變更。

    預設設定檔 ID (選用)

    在下拉式清單中選擇設定檔,此設定檔會對應到 Content type (內容類型) 欄位中的內容類型。

    內容格式 (無法變更)

    內容格式設定為 URLencoded,無法變更。

  3. 如果您要變更下列選項的 CloudFront 預設行為,請選取適當的核取方塊。

    當請求的內容類型未作設定時,請轉發請求到原始來源

    如果您尚未指定用於請求內容類型的設定檔,而要允許請求轉傳到您的原始伺服器,請勾選此核取方塊。

    使用提供的查詢參數覆寫內容類型的設定檔

    如果您要允許查詢引數中所提供的設定檔,覆寫您針對內容類型指定的設定檔,請勾選此核取方塊。

  4. 如果您選取核取方塊,以允許查詢參數來覆寫預設的設定檔,則必須完成組態的下列其他欄位。在這些查詢參數對應中,您最多可以建立五個,以便與查詢一起使用。

    查詢參數

    輸入要包含在 URL 中以用於 fle-profile 查詢參數的值。此值指示使CloudFront 用與此查詢引數相關聯的設定檔 ID (您在下一個欄位中指定),以進行此查詢的欄位層級加密。

    最多可使用 128 個字元。該值不能包含空格,而且必須使用英數字元或以下字元:破折號 (-)、句點 (.)、底線 (_)、星號 (*)、加號 (+)、百分比 (%)。

    設定檔 ID

    在下拉式清單中選擇設定檔,您要將此設定檔與您針對 Query argument (查詢引數) 輸入的值建立關聯。

    當查詢參數中指定的設定檔不存在時,將請求轉發到原始來源

    如果您要允許在查詢引數中指定的描述檔未在中定義要求移至您的來源,請選取此核取方塊 CloudFront。

步驟 5:將組態新增到快取行為

若要使用欄位層級加密,請透過將組態 ID 新增為分佈的值,將組態連結到分佈的快取行為。

重要

若要將欄位層級的加密設定連結至快取行為,必須將分佈設定為永遠使用 HTTPS,並接受來自瀏覽者的 HTTP POSTPUT 請求。也就是說,下列條件必須為真:

  • 快取行為的 Viewer Protocol 政策 (檢視器通訊協定政策) 必須設定為將 Redirect HTTP to HTTPS (HTTP 重新引導至 HTTPS)HTTPS Only (僅 HTTPS)。(在 AWS CloudFormation 或 CloudFront API 中,ViewerProtocolPolicy必須設定為redirect-to-httpshttps-only。)

  • 快取行為的允許的 HTTP 方法必須設為 GET、HEAD、OPTIONS、PUT、POST、PATCH、DELETE。(在 AWS CloudFormation 或 CloudFront API 中,AllowedMethods必須設定為GETHEADOPTIONSPUTPOSTPATCHDELETE。 這些可以以任何順序指定。)

  • 原始伺服器設定的 Origin Protocol 政策 (原始伺服器通訊協定政策) 必須設定為 Match Viewer (符合檢視器)HTTPS Only (僅 HTTPS)。(在 AWS CloudFormation 或 CloudFront API 中,OriginProtocolPolicy必須設定為match-viewerhttps-only。)

如需詳細資訊,請參閱 發佈設定參考

在您的來源解密資料欄位

CloudFront 使用加密資料欄位。AWS Encryption SDK資料在整個應用程式堆疊中保持加密狀態,只能由具有解密憑證的應用程式存取。

加密後,加密文字是 base64 編碼的。當您的應用程式在原始伺服器解密文字時,必須先對加密文字解碼,然後使用 AWS 加密開發套件來解密資料。

以下程式碼範例說明應用程式如何在原始伺服器中解密資料。注意下列事項:

  • 為了簡化範例,本範例從工作目錄中的檔案載入公有金鑰和私有金鑰 (以 DER 格式)。在實務上,您可以將私有金鑰存放在安全的離線位置,例如離線硬體安全模組中,並將公有金鑰分佈到您的開發團隊。

  • CloudFront 在加密數據時使用特定信息,並且應在原始位置使用相同的參數集來對其進行解密。初始化時 CloudFront使用的參數 MasterKey 包括以下內容:

    • PROVIDER_NAME:當您建立欄位層級加密設定檔時指定了這個值。在這裡使用相同的值。

    • KEY_NAME:您在上傳公開金鑰時建立了一個名稱 CloudFront,然後在設定檔中指定金鑰名稱。在這裡使用相同的值。

    • 算法: CloudFront 用RSA/ECB/OAEPWithSHA-256AndMGF1Padding作加密算法,因此您必須使用相同的算法來解密數據。

  • 如果您以加密文字做為輸入來執行以下範例程式時,則解密的資料將輸出到您的主控台。如需詳細資訊,請參閱 AWS 加密 SDK 中的 Java 範例程式碼

範本程式碼

import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import org.apache.commons.codec.binary.Base64; import com.amazonaws.encryptionsdk.AwsCrypto; import com.amazonaws.encryptionsdk.CryptoResult; import com.amazonaws.encryptionsdk.jce.JceMasterKey; /** * Sample example of decrypting data that has been encrypted by CloudFront field-level encryption. */ public class DecryptExample { private static final String PRIVATE_KEY_FILENAME = "private_key.der"; private static final String PUBLIC_KEY_FILENAME = "public_key.der"; private static PublicKey publicKey; private static PrivateKey privateKey; // CloudFront uses the following values to encrypt data, and your origin must use same values to decrypt it. // In your own code, for PROVIDER_NAME, use the provider name that you specified when you created your field-level // encryption profile. This sample uses 'DEMO' for the value. private static final String PROVIDER_NAME = "DEMO"; // In your own code, use the key name that you specified when you added your public key to CloudFront. This sample // uses 'DEMOKEY' for the key name. private static final String KEY_NAME = "DEMOKEY"; // CloudFront uses this algorithm when encrypting data. private static final String ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; public static void main(final String[] args) throws Exception { final String dataToDecrypt = args[0]; // This sample uses files to get public and private keys. // In practice, you should distribute the public key and save the private key in secure storage. populateKeyPair(); System.out.println(decrypt(debase64(dataToDecrypt))); } private static String decrypt(final byte[] bytesToDecrypt) throws Exception { // You can decrypt the stream only by using the private key. // 1. Instantiate the SDK final AwsCrypto crypto = new AwsCrypto(); // 2. Instantiate a JCE master key final JceMasterKey masterKey = JceMasterKey.getInstance( publicKey, privateKey, PROVIDER_NAME, KEY_NAME, ALGORITHM); // 3. Decrypt the data final CryptoResult <byte[], ? > result = crypto.decryptData(masterKey, bytesToDecrypt); return new String(result.getResult()); } // Function to decode base64 cipher text. private static byte[] debase64(final String value) { return Base64.decodeBase64(value.getBytes()); } private static void populateKeyPair() throws Exception { final byte[] PublicKeyBytes = Files.readAllBytes(Paths.get(PUBLIC_KEY_FILENAME)); final byte[] privateKeyBytes = Files.readAllBytes(Paths.get(PRIVATE_KEY_FILENAME)); publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(PublicKeyBytes)); privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); } }