

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

# 使用欄位層級加密來協助保護敏感資料
<a name="field-level-encryption"></a>

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

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

若要使用欄位層級加密，則可以設定 CloudFront 分佈以指定要加密的 POST 請求中的一組欄位，以及對其加密的公有金鑰。您可以在請求中加密多達 10 個資料欄位。(您無法使用欄位層級加密對請求中的所有資料進行加密；您必須指定要加密的各個欄位。)

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

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

![\[CloudFront 中的現場層級加密\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/fleoverview.png)


CloudFront 欄位層級加密使用非對稱加密，也稱為公有金鑰加密。您提供了 CloudFront 的公有金鑰，所指定的所有敏感資料都會進行自動加密。您提供給 CloudFront 的金鑰不能用於解密加密的值；只有您的私有金鑰可以做到這一點。

![\[只將敏感資料加密\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/encryptedfields.png)


**Topics**
+ [欄位層級加密概觀](#field-level-encryption-overview)
+ [設定欄位層級加密](#field-level-encryption-setting-up)
+ [在您的原始伺服器解密資料欄位](#field-level-encryption-decrypt)

## 欄位層級加密概觀
<a name="field-level-encryption-overview"></a>

以下步驟概述了設定欄位層級加密。對於特定的步驟，請參閱[設定欄位層級加密](#field-level-encryption-setting-up)。

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

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

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

1. **至快取行為的連結。**將組態連結到分佈的快取行為，以指定 CloudFront 應該何時加密資料。

## 設定欄位層級加密
<a name="field-level-encryption-setting-up"></a>

依照以下步驟開始使用欄位層級加密。若要了解欄位層級加密的配額 (先前稱為限制)，請參閱 [配額](cloudfront-limits.md)。
+ [步驟 1：建立 RSA 金鑰對](#field-level-encryption-setting-up-step1)
+ [步驟 2：將您的公有金鑰新增到 CloudFront](#field-level-encryption-setting-up-step2)
+ [步驟 3：建立欄位層級加密的設定檔。](#field-level-encryption-setting-up-step3)
+ [步驟 4：建立組態](#field-level-encryption-setting-up-step4)
+ [步驟 5：將組態新增到快取行為](#field-level-encryption-setting-up-step5)

### 步驟 1：建立 RSA 金鑰對
<a name="field-level-encryption-setting-up-step1"></a>

若要開始使用，您必須建立包含公有金鑰和私有金鑰的 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
<a name="field-level-encryption-setting-up-step2"></a>

取得您的 RSA 金鑰對之後，將公有金鑰新增到 CloudFront。<a name="field-level-encryption-setting-up-step2-procedure"></a>

**將您的公有金鑰新增至 CloudFront (主控台)**

1. 登入 AWS 管理主控台 ，並在 開啟 CloudFront 主控台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在導覽窗格中，選擇 **Public key** (公有金鑰)。

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

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

1. 針對 **Key value** (鍵值)，貼上公有金鑰的編碼鍵值 (包括 `-----BEGIN PUBLIC KEY-----` 和 `-----END PUBLIC KEY-----` 行)。

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

1. 選擇 **Add** (新增)。

透過重複程序中的步驟，您可以新增更多用於 CloudFront 的金鑰。

### 步驟 3：建立欄位層級加密的設定檔。
<a name="field-level-encryption-setting-up-step3"></a>

將至少一個公有金鑰新增到 CloudFront 後，建立設定檔，告知 CloudFront 要加密哪些欄位。<a name="field-level-encryption-setting-up-step3-procedure"></a>

**建立欄位層級加密的設定檔 (主控台)**

1. 在導覽窗格中，選擇 **Field-level encryption (欄位層級加密)**。

1. 選擇 **Create profile (建立設定檔)**。

1. 填寫下列欄位：  
**設定檔名稱**  
請輸入設定檔的專屬名稱。名稱不能有空格，並且只能包含英數字元、底線 (\$1) 和 連字號 (-)。最大字元數為 128。  
**公有金鑰名稱**  
在下拉式清單中，選擇您在步驟 2 中新增到 CloudFront 公有金鑰的名稱。CloudFront 使用金鑰來加密您在此設定檔中指定欄位。  
**供應商名稱**  
輸入片語以協助識別您的金鑰，例如您取得到金鑰對的供應商。當應用程式解密資料欄位時，將需要此資訊以及私有金鑰。供應商名稱不能有空格，並且只能包含英數字元、冒號 (:)、底線 (\$1) 和 連字號 (-)。最大字元數為 128。  
**欄位名稱模式符合**  
輸入您希望 CloudFront 加密的資料欄位的名稱，或識別請求中資料欄位名稱的模式。選擇 \$1 選項來新增您想要使用此金鑰加密的所有欄位。  
對於欄位名稱模式，您可以輸入資料欄位的整個名稱，例如 DateOfBirth，或僅使用萬用字元 (\$1) 的名稱的第一部分，例如 CreditCard\$1。除了可選的萬用字元 (\$1) 外，欄位名稱模式必須只包含英數字元、方括號 ([和])、句號 (.)、底線 (\$1) 和連字號 (-)。  
請確定不要為不同的欄位名稱模式使用重疊的字元。例如，如果您有 ABC\$1 的欄位名稱模式，則不能新增另一個 AB\$1 的欄位名稱模式。此外，欄位名稱區分大小寫，可以使用的字元數上限為 128。  
**註解**  
(選用) 輸入有關此設定檔的評論。最多可使用 128 個字元。

1. 填寫完欄位後，選擇 **Create profile (建立設定檔)**。

1. 如果您想新增更多設定檔，請選擇 **Add profile (新增設定檔)**。

### 步驟 4：建立組態
<a name="field-level-encryption-setting-up-step4"></a>

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

例如，當 CloudFront 無法加密資料時，可以指定在以下情況下，CloudFront 是否應該封鎖或轉送請求到您的原始伺服器：
+ **當請求的內容類型不在組態中** – 如果您尚未在組態中新增內容類型，則可以指定 CloudFront 是否應將具有該內容類型的請求轉傳到原始伺服器，而不加密資料欄位，或封鎖請求並傳回一個錯誤。
**注意**  
如果將內容類型新增到組態中，但未指定與該類型一起使用的設定檔，則具有該內容類型的 CloudFront 將一律把請求轉傳到原始伺服器。
+ **當查詢引數中提供的設定檔名稱未知時** – 當您使用分佈中不存在的設定檔名稱指定 `fle-profile` 查詢引數時，您可以指定 CloudFront 是否應將請求發送到原始伺服器，而不加密資料欄位，或封鎖請求並傳回錯誤。

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

因此，例如，您可以指定 (在您的組態中) **SampleProfile** 做為要使用的查詢參引數設定檔。然後，您可以使用 URL `https://d1234.cloudfront.net?fle-profile=SampleProfile` 而非 `https://d1234.cloudfront.net`，來讓 CloudFront 針對此請求使用 **SampleProfile**，而非使用您為請求的內容類型所建立的設定檔。

您最多可以為單一帳戶建立 10 個組態，然後將其中一個組態與的該帳戶的任何分佈的快取行為相關聯。<a name="field-level-encryption-setting-up-step4-procedure"></a>

**建立欄位層級加密的組態 (主控台)**

1. 在 **Field-level encryption (欄位層級加密)** 頁面，選擇 **Create configuration (建立組態)**。

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

1. 請填寫以下欄位指定要使用的設定檔。(有些欄位無法變更。)  
**內容類型 (無法變更)**  
內容類型設定為 `application/x-www-form-urlencoded`，無法變更。  
**預設設定檔 ID (選用)**  
在下拉式清單中選擇設定檔，此設定檔會對應到 **Content type (內容類型)** 欄位中的內容類型。  
**內容格式 (無法變更)**  
內容格式設定為 `URLencoded`，無法變更。

1. 如果您想要變更以下選項的 CloudFront 預設行為，請選取合適的核取方塊。  
**當請求的內容類型未作設定時，請轉發請求到原始來源**  
*如果您尚未指定用於請求內容類型的設定檔*，而要允許請求轉傳到您的原始伺服器，請勾選此核取方塊。  
**使用提供的查詢參數覆寫內容類型的設定檔**  
如果您要允許查詢引數中所提供的設定檔，*覆寫您針對內容類型指定的設定檔*，請勾選此核取方塊。

1. 如果您選取核取方塊，以允許查詢參數來覆寫預設的設定檔，則必須完成組態的下列其他欄位。在這些查詢參數對應中，您最多可以建立五個，以便與查詢一起使用。  
**查詢參數**  
輸入要包含在 URL 中以用於 `fle-profile` 查詢參數的值。這個值通知 CloudFront 使用與此查詢參數相關聯的設定檔 ID (在下一個欄位中指定的)，以用於此查詢的欄位層級加密。  
最多可使用 128 個字元。該值不能包含空格，而且必須使用英數字元或以下字元：破折號 (-)、句點 (.)、底線 (\$1)、星號 (\$1)、加號 (\$1)、百分比 (%)。  
**設定檔 ID**  
在下拉式清單中選擇設定檔，您要將此設定檔與您針對 **Query argument (查詢引數)** 輸入的值建立關聯。  
**當查詢參數中指定的設定檔不存在時，將請求轉發到原始來源**  
*如果在 CloudFront 中未定義查詢參數中指定的設定檔*，且如果您想要允許請求轉到原始伺服器，請選取核取方塊。

### 步驟 5：將組態新增到快取行為
<a name="field-level-encryption-setting-up-step5"></a>

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

**重要**  
若要將欄位層級的加密設定連結至快取行為，必須將分佈設定為永遠使用 HTTPS，並接受來自瀏覽者的 HTTP `POST` 和 `PUT` 請求。也就是說，下列條件必須為真：  
快取行為的 **Viewer Protocol 政策 (檢視器通訊協定政策)** 必須設定為將 **Redirect HTTP to HTTPS (HTTP 重新引導至 HTTPS)** 或 **HTTPS Only (僅 HTTPS)**。（在 CloudFormation 或 CloudFront API 中， `ViewerProtocolPolicy` 必須設定為 `redirect-to-https`或 `https-only`。)
快取行為的**允許的 HTTP 方法**必須設為 **GET、HEAD、OPTIONS、PUT、POST、PATCH、DELETE**。（在 CloudFormation 或 CloudFront API 中， `AllowedMethods` 必須設定為 `GET`、`HEAD`、`OPTIONS`、`PUT`、`POST`、`PATCH`、`DELETE`。 這些可以依任何順序指定。)
原始伺服器設定的 **Origin Protocol 政策 (原始伺服器通訊協定政策)** 必須設定為 **Match Viewer (符合檢視器)** 或 **HTTPS Only (僅 HTTPS)**。（在 CloudFormation 或 CloudFront API 中， `OriginProtocolPolicy` 必須設定為 `match-viewer`或 `https-only`。)

如需詳細資訊，請參閱[所有分佈設定參考](distribution-web-values-specify.md)。

## 在您的原始伺服器解密資料欄位
<a name="field-level-encryption-decrypt"></a>

CloudFront 透過使用 [AWS Encryption SDK](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/introduction.html) 加密資料欄位。資料在整個應用程式堆疊中保持加密狀態，只能由具有解密憑證的應用程式存取。

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

以下程式碼範例說明應用程式如何在原始伺服器中解密資料。注意下列事項：
+ 為了簡化範例，本範例從工作目錄中的檔案載入公有金鑰和私有金鑰 (以 DER 格式)。在實務上，您可以將私有金鑰存放在安全的離線位置，例如離線硬體安全模組中，並將公有金鑰分佈到您的開發團隊。
+ CloudFront 在加密資料時使用特定的資訊，並且應該在原始伺服器上使用相同的一組參數對其進行解密。在初始化 MasterKey 時使用的參數 CloudFront 包括下列項目：
  + PROVIDER\$1NAME：當您建立欄位層級加密設定檔時指定了這個值。在這裡使用相同的值。
  + KEY\$1NAME：當您將公有金鑰上傳至 CloudFront 時，您為其建立一個名稱，然後在設定檔中指定了金鑰名稱。在這裡使用相同的值。
  + ALGORITHM：CloudFront 使用 `RSA/ECB/OAEPWithSHA-256AndMGF1Padding` 做為加密演算法，因此您必須使用相同的演算法來解密資料。
+ 如果您以加密文字做為輸入來執行以下範例程式時，則解密的資料將輸出到您的主控台。如需詳細資訊，請參閱 AWS 加密 SDK 中的 [Java 範例程式碼](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/java-example-code.html)。

### 範本程式碼
<a name="field-level-encryption-decrypt-sample"></a>

```
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));
    }
}
```