필드 수준 암호화를 사용하여 민감한 데이터 보호
Amazon CloudFront를 사용하면 HTTPS를 통해 오리진 서버에 대한 종단 간 보안 연결을 적용할 수 있습니다. 필드 레벨 암호화는 추가 보안 레이어를 추가하여 시스템 처리 전체에서 특정 데이터를 보호하고 특정 애플리케이션만 이를 볼 수 있도록 합니다.
필드 레벨 암호화를 통해 사용자가 민감한 정보를 웹 서버에 안전하게 업로드할 수 있습니다. 사용자가 제공한 민감한 정보는 사용자에게 가까운 엣지에서 암호화되고 전체 애플리케이션 스택에서 암호화를 유지하므로 데이터가 필요하고 이를 해독할 자격 증명을 보유한 애플리케이션만 이 작업을 수행할 수 있습니다.
필드 레벨 암호화를 사용하려면 CloudFront 배포를 구성하여 암호화되기를 원하는 POST 요청의 필드 세트와 암호화에 사용할 퍼블릭 키를 지정할 수 있습니다. 한 요청에서 최대 10개의 데이터 필드를 암호화할 수 있습니다. (필드 레벨 암호화된 요청의 모든 데이터를 암호화할 수는 없습니다. 암호화할 개별 필드를 지정해야 합니다.)
필드 레벨 암호화가 포함된 HTTPS 요청이 오리진에 전달될 때 요청은 오리진 애플리케이션 또는 하위 시스템 전체에 라우팅되고, 민감한 데이터는 계속 암호화되어 민감한 데이터의 침해 또는 우발적 데이터 손실의 위험을 줄입니다. 크레딧 번호에 대한 액세스를 필요로 하는 결제 처리 시스템과 같이 업무상 사유에 따라 민감한 데이터에 대한 액세스를 필요로 하는 구성 요소는 적절한 프라이빗 키를 사용하여 해독하고 데이터에 액세스할 수 있습니다.
참고
필드 레벨 암호화를 사용하려면 오리진이 chunked 인코딩을 지원해야 합니다.
CloudFront 필드 레벨 암호화는 퍼블릭 키 암호화라고도 하는 비대칭 암호화를 사용합니다. CloudFront에 퍼블릭 키를 입력하면 지정한 모든 민감한 데이터가 자동적으로 암호화됩니다. CloudFront에 입력한 키는 암호화된 값의 해독에 사용할 수 없습니다. 이는 프라이빗 키만이 가능합니다.
필드 레벨 암호화 개요
다음 단계는 필드 레벨 암호화 설정의 개요를 제공합니다. 자세한 단계는 필드 레벨 암호화 설정 단원을 참조하십시오.
-
퍼블릭 키-프라이빗 키 페어를 가져옵니다. CloudFront에서 필드 레벨 암호화를 설정하기 전에 퍼블릭 키를 얻고 이를 추가해야 합니다.
-
필드 레벨 암호화 프로필을 생성합니다. CloudFront에서 생성한 필드 레벨 암호화 프로필은 암호화하고자 하는 필드를 정의합니다.
-
필드 레벨 암호화 구성을 생성합니다. 구성은 요청의 콘텐츠 유형 또는 쿼리 인수를 기반으로 특정 데이터 필드 암호화에 사용할 프로필을 지정합니다. 여러 시나리오에 대해 원하는 요청 전달 동작 옵션을 선택할 수도 있습니다. 예를 들어 요청 URL의 쿼리 인수로 지정된 프로파일 이름이 CloudFront에 없는 경우에 대한 동작을 설정할 수 있습니다.
-
캐시 동작에 연결합니다. 배포에 대한 캐시 동작의 구성을 연결하여 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 키 페어를 얻은 후 퍼블릭 키를 CloudFront에 추가합니다.
CloudFront에 퍼블릭 키를 추가하려면(콘솔)
AWS Management Console에 로그인한 다음 https://console.aws.amazon.com/cloudfront/v4/home
에서 CloudFront 콘솔을 엽니다. -
탐색 창에서 퍼블릭 키를 선택합니다.
-
Add public key(퍼블릭 키 추가)를 선택합니다.
-
키 이름에 키의 고유 이름을 입력합니다. 이름은 공백을 포함할 수 없으며, 영숫자 문자, 밑줄(_) 및 하이픈(-)만 포함할 수 있습니다. 문자는 최대 128자입니다.
-
Key value(키 값)에
-----BEGIN PUBLIC KEY-----
및-----END PUBLIC KEY-----
행을 포함한 퍼블릭 키의 암호화된 키 값을 붙여 넣습니다. -
설명에 코멘트를 추가합니다(선택 사항). 예를 들어, 퍼블릭 키의 만료 날짜를 포함할 수 있습니다.
-
추가를 선택합니다.
이 절차의 단계를 반복하여 CloudFront에서 사용할 키를 더 추가할 수 있습니다.
3단계: 필드 레벨 암호화 프로필 생성
CloudFront에 하나 이상의 퍼블릭 키를 추가한 이후 CloudFront에 어떤 필드를 암호화할지 알리는 프로필을 생성합니다.
필드 레벨 암호화 프로필을 생성하려면(콘솔)
-
왼쪽 탐색 창에서 Field-level encryption(필드 레벨 암호화)을 선택합니다.
-
Create profile(프로파일 생성)을 선택합니다.
-
다음 필드를 입력합니다.
- 프로필 이름
-
프로필의 고유한 이름을 입력합니다. 이름은 공백을 포함할 수 없으며, 영숫자 문자, 밑줄(_) 및 하이픈(-)만 포함할 수 있습니다. 문자는 최대 128자입니다.
- 퍼블릭 키 이름
-
드롭다운 목록에서, 2단계에서 CloudFront에 추가한 퍼블릭 키의 이름을 선택합니다. CloudFront는 키를 사용하여 이 프로필에서 지정한 필드를 암호화합니다.
- 공급자 이름
-
키 페어를 얻은 공급자와 같이 키를 식별하는 데 도움이 될 구문을 입력합니다. 프라이빗 키와 함께 이 정보는 애플리케이션이 데이터 필드를 해독할 때 필요합니다. 공급자 이름은 공백을 포함할 수 없으며, 영숫자 문자, 콜론(:), 밑줄(_) 및 하이픈(-)만 포함할 수 있습니다. 문자는 최대 128자입니다.
- 일치시킬 필드 이름 패턴
-
CloudFront에서 암호화하고자 하는 데이터 필드의 이름 또는 요청에서 데이터 필드 이름을 식별하는 패턴을 입력합니다. + 옵션을 사용하여 이 키를 사용하여 암호화하고자 하는 모든 필드를 추가합니다.
필드 이름 패턴의 경우 데이터 필드의 전체 이름(예: DateOfBirth)을 입력하거나 와일드카드 문자(*)를 포함하여 이름의 첫 부분(예: CreditCard*)만을 입력할 수 있습니다. 필드 이름 패턴은 영숫자 문자, 대괄호([ 및 ]), 마침표(.), 밑줄(_) 및 하이픈(-)만을 포함할 수 있으며, 추가로 와일드카드 문자(*)를 선택적으로 포함할 수 있습니다.
다른 필드 이름 패턴과 중첩되는 문자를 사용하지 마세요. 예를 들어, ABC*라는 필드 이름 패턴을 보유한 경우 또 다른 AB* 필드 이름 패턴을 추가할 수는 없습니다. 또한 필드 이름은 대소문자를 구분하며, 사용할 수 있는 최대 문자는 128자입니다.
- Comment
-
(선택 사항) 이 프로필에 대한 설명을 입력합니다. 사용할 수 있는 최대 문자 수는 128자입니다.
-
필드에 내용을 입력한 후 Create profile(프로파일 생성)을 선택합니다.
-
프로필을 더 추가하고자 하는 경우 프로필 추가를 선택합니다.
4단계: 구성 생성
하나 이상의 필드 레벨 암호화 프로필을 생성한 이후, 암호화할 데이터를 포함하는 요청의 콘텐츠 유형, 암호화에 사용할 프로필, CloudFront의 암호화 처리 방법을 지정하는 기타 옵션을 지정하는 구성을 생성합니다.
예를 들어, CloudFront가 데이터를 암호화할 수 없을 때 다음 시나리오에서 CloudFront가 요청을 차단하거나 오리진으로 전달할지 여부를 지정할 수 있습니다.
-
요청의 콘텐츠 유형이 구성에 없을 때 – 구성에 콘텐츠 유형을 추가하지 않은 경우 CloudFront가 해당 콘텐츠 유형을 포함한 요청을 데이터 필드 암호화 없이 오리진에 전달할지 아니면 요청을 차단하고 오류를 반환할지 지정할 수 있습니다.
참고
구성에 콘텐츠 유형을 추가했지만 해당 유형을 사용할 프로필을 지정하지 않은 경우 CloudFront에서 해당 콘텐츠 유형을 포함한 요청을 항상 오리진으로 전달합니다.
-
쿼리 인수에 입력된 프로필 이름이 알 수 없는 이름임 – 배포에 존재하지 않는 프로필 이름을 포함하는
fle-profile
쿼리 인수를 지정할 때 CloudFront가 요청을 데이터 필드 암호화 없이 오리진으로 보낼지 아니면 요청을 차단하고 오류를 반환할지 지정할 수 있습니다.
구성에서 URL에 쿼리 인수로 프로필을 입력함으로써 해당 쿼리에 대한 콘텐츠 유형으로 매핑한 프로필을 무시할지 여부를 지정할 수도 있습니다. 기본적으로 CloudFront는 콘텐츠 유형에 매핑한 프로필을 사용합니다(이를 지정한 경우). 이를 통해 기본적으로 사용할 프로필을 가질 수 있지만 다른 프로필을 적용하고자 하는 특정 요청을 정할 수도 있습니다.
예를 들어, 사용할 쿼리 인수로 (구성에 있는) SampleProfile
을 지정할 수 있습니다. 그런 다음, https://d1234.cloudfront.net?fle-profile=SampleProfile
대신 URL https://d1234.cloudfront.net
을 사용하여 CloudFront가 요청의 콘텐츠 유형에 대해 설정한 프로필 대신 이 요청에서 SampleProfile
을 사용하도록 할 수 있습니다.
단일 계정에 대해 최대 10개의 구성을 생성할 수 있으며 계정에 대한 모든 배포의 캐시 동작에 구성 중 하나를 연결할 수 있습니다.
필드 레벨 암호화 구성을 생성하려면(콘솔)
-
Field-level encryption(필드 레벨 암호화) 페이지에서 구성 만들기를 선택합니다.
참고: 최소 하나의 프로필을 생성하지 않은 경우 구성 생성 옵션이 표시되지 않습니다.
-
다음 필드를 입력하여 사용할 프로필을 지정합니다. (일부 필드는 변경할 수 없습니다.)
- 콘텐츠 유형(변경할 수 없음)
-
콘텐츠 유형은
application/x-www-form-urlencoded
로 설정되며 변경할 수 없습니다. - 기본 프로필 ID(선택 사항)
-
드롭다운 목록 가운데 콘텐츠 유형 필드에서 콘텐츠 유형을 매핑하고자 하는 프로필을 선택합니다.
- 콘텐츠 형식(변경할 수 없음)
-
콘텐츠 형식은
URLencoded
로 설정되며 변경할 수 없습니다.
-
다음 옵션에 대한 CloudFront 기본 동작을 변경하고자 하는 경우 해당 확인란을 선택합니다.
- 요청의 콘텐츠 유형이 구성되지 않을 때 오리진으로 요청 전달
-
요청의 콘텐츠 유형에 대해 사용할 프로필을 지정하지 않았다면 요청이 오리진으로 이동하도록 허용할 경우 확인란을 선택합니다.
- 입력된 쿼리 인수를 포함한 콘텐츠 유형에 대한 프로필 무시
-
쿼리 인수에 입력된 프로필이 콘텐츠 유형에 대해 지정한 프로필을 무시하도록 허용할 경우 확인란을 선택합니다.
-
확인란을 선택하여 쿼리 인수가 기본 프로필을 무시하도록 허용할 경우 구성에 대한 다음 추가 필드를 입력해야 합니다. 쿼리에 사용할 쿼리 인수 매핑 가운데 최대 5개를 생성할 수 있습니다.
- 쿼리 인수
-
fle-profile
쿼리 인수에 대해 URL에서 포함하고자 하는 값을 입력합니다. 이 값은 CloudFront에게 이 쿼리의 필드 레벨 암호화에 대한 쿼리 인수와 연결된 프로필 ID(다음 필드에서 지정)를 사용하도록 합니다.사용할 수 있는 최대 문자 수는 128자입니다. 값에는 공백이 포함될 수 없으며, 영숫자 문자와 다음 문자만을 사용할 수 있습니다. 대시(-), 마침표(.), 밑줄(_), 별표(*) 더하기 기호(+), 퍼센트(%).
- 프로필 ID
-
드롭다운 목록 가운데 쿼리 인수에 입력한 값과 연결하고자 하는 프로필을 선택합니다.
- 쿼리 인수에 지정된 프로필이 존재하지 않을 때 요청을 오리진에 전달
-
쿼리 인수에서 지정된 프로필이 CloudFront에서 정의되지 않은 경우 요청이 오리진으로 이동하도록 허용할 경우 확인란을 선택합니다.
5단계: 캐시 동작에 구성 추가
필드 레벨 암호화를 사용하려면 배포에 대한 값으로 구성 ID를 추가함으로써 배포에 대한 캐시 동작과 구성을 연결합니다.
중요
필드 레벨 암호화 구성을 캐시 동작에 연결하려면 항상 HTTPS를 사용하고 최종 사용자의 HTTP POST
및 PUT
요청을 수락하도록 배포를 구성해야 합니다. 즉, 다음 조건이 충족되어야 합니다.
-
캐시 동작의 Viewer Protocol Policy(최종 사용자 프로토콜 정책)을 Redirect HTTP to HTTPS(HTTP에서 HTTPS로 리디렉션) 또는 HTTPS Only(HTTPS만)로 설정해야 합니다. (AWS CloudFormation 또는 CloudFront API에서는
ViewerProtocolPolicy
를redirect-to-https
또는https-only
로 설정해야 합니다.) -
캐시 동작의 Allowed HTTP Methods(허용되는 HTTP 메서드)를 GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE로 설정해야 합니다. (AWS CloudFormation 또는 CloudFront API에서
AllowedMethods
를GET
,HEAD
,OPTIONS
,PUT
,POST
,PATCH
,DELETE
로 설정해야 합니다. 이러한 값은 임의의 순서로 지정할 수 있습니다.) -
오리진 설정의 Origin Protocol Policy(오리진 프로토콜 정책)를 Match Viewer(최종 사용자와 일치) 또는 HTTPS Only(HTTPS만)로 설정해야 합니다. (AWS CloudFormation 또는 CloudFront API에서는
OriginProtocolPolicy
를match-viewer
또는https-only
로 설정해야 합니다.)
자세한 내용은 배포 설정 참조 단원을 참조하십시오.
오리진의 데이터 필드 해독
CloudFront는 AWS Encryption SDK를 사용하여 데이터 필드를 암호화합니다. 데이터는 애플리케이션 스택에 걸쳐 암호화를 유지하며, 이를 해독할 자격 증명을 보유한 애플리케이션만이 액세스할 수 있습니다.
암호화 이후 암호 텍스트는 base64 암호화됩니다. 애플리케이션이 오리진의 텍스트를 해독할 때 우선 암호 텍스트를 디코딩한 다음 AWS 암호화 SDK를 사용하여 데이터를 해독해야 합니다.
다음 코드 예제는 애플리케이션이 오리진의 데이터를 해독하는 방법을 보여줍니다. 다음을 참조하세요.
-
예제를 간소화하기 위해 이 샘플은 작업 디렉터리에 있는 파일에서 퍼블릭 및 프라이빗 키(DER 형식)를 로드합니다. 실제로는 프라이빗 키를 오프라인 하드웨어 보안 모듈과 같은 안전한 오프라인 위치에 저장하고, 퍼블릭 키를 개발 팀에 배포합니다.
-
CloudFront는 데이터를 암호화하는 동안 특정 정보를 사용하며 동일한 파라미터 세트를 오리진에서 사용하여 이를 해독해야 합니다. MasterKey를 초기화하는 동안 CloudFront가 사용하는 파라미터는 다음을 포함합니다.
-
PROVIDER_NAME: 필드 레벨 암호화 프로필을 생성할 때 이 값을 지정했습니다. 여기에서 동일한 값을 사용합니다.
-
KEY_NAME: 퍼블릭 키를 CloudFront로 업로드할 때 퍼블릭 키의 이름을 생성했으며, 프로필에서 키 이름을 지정했습니다. 여기에서 동일한 값을 사용합니다.
-
ALGORITHM: 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)); } }