Amazon CloudFront
开发人员指南 (API 版本 2016-09-29)
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

使用字段级加密帮助保护敏感数据

您已将 CloudFront 配置为使用 HTTPS 帮助对与源服务器的端到端连接强制实施保护。字段级加密增加了一个额外的安全保护层以及 HTTPS,使您可以在整个系统处理过程中保护特定的数据,以便只有某些应用程序才能查看它。

使用字段级加密可以安全地向您的 Web 服务器上传用户提交的敏感信息。客户端提供的敏感信息在更接近用户的边缘站点中进行加密,这些敏感信息在整个应用程序堆栈中保持加密状态,从而确保仅需要该数据(以及具有用于解密的凭证)的应用程序才能使用该数据。——

要使用字段级加密,您可以配置您的 CloudFront 分配,以指定要加密的 POST 请求中的字段集合以及用于加密它们的公有密钥。您最多可以在一个请求中加密 10 个数据字段。(您无法使用字段级加密在一个请求中加密所有数据;您必须指定要加密的各个字段。)

如果带有字段级加密的 HTTPS 请求转发到源,并且请求路由经过您的整个源子系统,则敏感数据仍然进行加密,从而降低敏感数据泄露或意外丢失的风险。出于业务原因需要访问敏感数据的组件 (例如需要访问信用号码的支付处理系统) 可以使用适当的私有密钥来解密和访问数据。

请注意,要使用字段级加密,源必须支持分块编码。


				CloudFront 中的字段级加密

CloudFront 字段级加密使用非对称加密,也称为公有密钥加密。您需要向 CloudFront 提供一个公有密钥,之后系统会自动加密您指定的所有敏感数据。不能使用您向 CloudFront 提供的密钥解密经过加密的值;只有您的私有密钥才能执行解密。


				仅加密敏感数据

字段级加密概览

以下步骤概述了如何设置字段级加密。有关具体步骤,请参阅设置字段级加密

  1. 获取公有密钥/私有密钥对。 您必须获取和添加公有密钥,然后再开始在 CloudFront 中设置字段级加密。

  2. 创建字段级加密配置文件。 在 CloudFront 中创建的字段级加密配置文件定义要加密的字段。

  3. 创建字段级加密配置。 配置指定用于加密特定数据字段的配置文件(根据请求的内容类型或查询参数)。——您也可以为不同场景选择请求转发行为选项;例如,当请求 URL 中的查询参数所指定的配置文件名称在 CloudFront 中不存在时。

  4. 链接到缓存行为。 将配置链接到分配的缓存行为以指定何时 CloudFront 应加密数据。

设置字段级加密

按照以下步骤操作,开始使用字段级加密。要了解字段级加密中的限制,请参阅限制

第 1 步:获取 RSA 密钥对

要开始使用,您必须获取一个包括公有密钥的 RSA 密钥对,这样 CloudFront 可以加密数据;您还必须获取一个私有密钥,这样源上的组件可以解密已经加密的字段。例如,您可以使用 OpenSSL 或其他工具来创建密钥对。密钥大小必须为 2048 位。有关更多信息,请参阅 创建 RSA 密钥对并将公有密钥上传到 AWS 管理控制台 中

第 2 步:将您的公有密钥添加到 CloudFront

获取 RSA 密钥对后,将您的公有密钥添加到 CloudFront。

将您的公有密钥添加到 CloudFront (控制台)

  1. 登录 AWS 管理控制台,通过以下网址打开 CloudFront 控制台:https://console.amazonaws.cn/cloudfront/

  2. 在导航窗格中,选择公有密钥

  3. 选择 Add public key

  4. 对于密钥名称,请为密钥键入唯一的名称。该名称不能包含空格,且只能包含字母数字字符、下划线 (_) 和连字符 (-)。最大字符数为 128。

  5. 对于加密的密钥,复制并粘贴公有密钥经过编码的密钥值,包括“-----BEGIN PUBLIC KEY-----”和“-----END PUBLIC KEY-----”行。

  6. 对于注释,请添加一个可选的注释。例如,您可以包含公有密钥的到期日期。

  7. 选择 Add key

您可以重复此过程中的步骤,添加更多密钥供 CloudFront 使用。

第 3 步:为字段级加密创建配置文件

在您至少将一个公有密钥添加到 CloudFront 后,创建一个配置文件,在其中告诉 CloudFront 加密哪些字段。

为字段级加密创建配置文件 (控制台)

  1. 在导航窗格中,选择字段级加密

  2. 选择 Create profile

  3. 填写以下字段:

    配置文件名称

    为配置文件键入唯一名称。该名称不能包含空格,且只能包含字母数字字符、下划线 (_) 和连字符 (-)。最大字符数为 128。

    公有密钥名称

    在下拉列表中,选择您在步骤 2 中添加到 CloudFront 的公有密钥的名称。CloudFront 使用该密钥加密在该配置文件中指定的字段。

    提供商名称

    键入短语以帮助识别密钥,例如从其获取密钥对的提供商。当应用程序解密数据字段时,需要该信息以及私有密钥。提供商名称不能包含空格,且只能包含字母数字字符、冒号 (:)、下划线 (_) 和连字符 (-)。最大字符数为 128。

    要匹配的字段名称模式

    键入数据字段的名称,或者键入用于识别请求中希望 CloudFront 加密的数据字段名称的模式。选择 + 选项以添加您想要使用此密钥加密的所有字段。

    对于字段名称模式,您可以键入数据字段的整个名称,如 DateOfBirth,或者仅键入名称的第一部分及通配符 (*),如 CreditCard*。除了可选的通配符 (*) 外,字段名称模式必须仅包含字母数字字符、方括号 ([ 和 ])、句点 (.)、下划线 (_) 和连字符 (-)。

    请确保您未针对不同的字段名称模式使用重叠字符。例如,如果您有一个字段名称模式 ABC*,您就不能再添加另一个字段名称模式 AB*。此外,请注意,字段名称区分大小写,并且您可以使用的最大字符数为 128。

    注释

    (可选)键入有关该配置文件的注释。您可以使用的最大字符数为 128。

  4. 在填写这些字段后,选择Create profile

  5. 如果您要添加更多配置文件,请选择添加配置文件

第 4 步:创建配置

在您创建一个或多个字段级加密配置文件后,请创建一个配置,以便指定请求的内容类型,包括要加密的数据、要用于加密的配置文件以及用于指定希望 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 个配置,然后将其中一个配置与该账户的任何分配的缓存行为相关联。

为字段级加密创建配置 (控制台)

  1. Field-level encryption (字段级加密) 页面上,选择 Create configuration

    注意:如果您尚未创建一个配置文件,您将不会看到配置创建选项。

  2. 填写以下字段以指定要使用的配置文件。(某些字段无法更改。)

    内容类型 (无法更改)

    内容类型将设置为 application/x-www-form-urlencoded 并且无法更改。

    默认配置文件 ID(可选)

    在下拉列表中,选择您要映射到 Content type (内容类型) 字段中的内容类型的配置文件。

    内容格式 (无法更改)

    内容格式将设置为 URLencoded 并且无法更改。

  3. 如果您要更改以下选项的默认 CloudFront 行为,请选择适当的复选框。

    当请求的内容类型未配置时,将请求转发到原始地址

    如果您要允许请求转到源,并且您尚未指定要用于请求的内容类型的配置文件,则选中该复选框。

    使用提供的查询参数覆盖内容类型的配置文件

    如果您要允许某个查询参数中提供的配置文件覆盖您为内容类型指定的配置文件,则选中该复选框。

  4. 如果您选中该复选框以允许查询参数覆盖默认配置文件,则您必须完成以下额外配置字段。您最多可以创建五个查询参数映射供查询使用。

    查询参数

    键入要包含在 fle-profile 查询参数的 URL 中的值。对于此查询的字段级加密,该值告诉 CloudFront 使用与此查询参数关联的配置文件 ID (在下一个字段中指定)。

    您可以使用的最大字符数为 128。该值不能包含空格,并且必须仅使用字母数字或以下字符:短划线 (-)、句点 (.)、下划线 (_)、星号 (*)、加号 (+)、百分号 (%)。

    配置文件 ID

    在下拉列表中,选择您要与为 Query argument (查询参数) 键入的值相关联的配置文件。

    当查询参数中指定的配置文件不存在时,将请求转发到源

    如果要允许请求转到源,并且没有在 CloudFront 中定义在查询参数中指定的配置文件,请选择该复选框。

第 5 步:将配置添加到缓存行为

要使用字段级加密,需要将配置链接到分配的缓存行为,方法为添加配置 ID 作为分配的值。请注意,查看器协议策略和源协议策略必须是 HTTPS,以便于您将配置链接到缓存行为。

有关更多信息,请参阅您创建或更新分配时指定的值

在源解密数据字段

CloudFront 使用 AWS 加密开发工具包加密数据字段。在您的整个应用程序堆栈中,数据保持加密,并且只能由具有用于解密数据的凭证的应用程序进行访问。

加密后,密码文本采用 Base64 编码。当您的应用程序解密源上的文本时,应用程序必须首先解码密码文本,然后使用 AWS 加密开发工具包解密数据。

以下代码示例说明了应用程序如何解密源上的数据。请注意以下几点:

  • 为简化示例,此示例从工作目录中的文件内加载公有密钥和私有密钥 (采用 DER 格式)。实际上,您会将私有密钥存储在安全的离线位置 (如离线硬件安全模块),并将公有密钥分配给您的开发团队。

  • CloudFront 在加密数据时使用特定信息,应在源上使用同一组参数进行解密。初始化主密钥时参数 CloudFront 包括以下内容:

    • PROVIDER_NAME:您在创建字段级加密配置文件时指定该值。在此处使用相同的值。

    • KEY_NAME:当您将公有密钥上传到 CloudFront 时创建了其名称,然后在配置文件中指定了密钥名称。在此处使用相同的值。

    • ALGORITHM:CloudFront 使用“RSA/ECB/OAEPWithSHA-256AndMGF1Padding”作为加密算法,因此,您必须使用相同的算法解密数据。

  • 如果您在运行以下示例程序时使用密码文本作为输入,则解密数据会输出到您的控制台。有关更多信息,请参阅 AWS 加密开发工具包中的 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)); } }