실습 예제 · 11
KMS 고객 관리형 키로 암호화
고객 관리형 키를 쓰는 이유는 암호화 자체가 아니라 키에 대한 통제와 감사입니다. 누가 이 키를 쓸 수 있는지 정하고, 언제 누가 썼는지 남기고, 교체 주기를 정하는 일이 전부 여기서 이뤄집니다. 이 실습에서는 봉투 암호화의 흐름도 데이터 키로 직접 확인합니다.
학습 목표
- 키 정책과 IAM 정책이 함께 작동하는 방식을 설명할 수 있습니다.
- 봉투 암호화의 단계를 데이터 키 생성과 복호화로 확인할 수 있습니다.
- 버킷 기본 암호화를 고객 관리형 키로 설정할 수 있습니다.
- 버킷 키가 무엇을 줄이는지 설명할 수 있습니다.
- 자동 교체가 적용되는 키 종류와 주기를 구분할 수 있습니다.
시나리오
규제 대상 문서를 S3 버킷에 보관합니다. 요구는 이렇습니다.
- 객체는 고객 관리형 키로 암호화되어야 합니다.
- 누가 언제 키를 썼는지 제시할 수 있어야 합니다.
- 키를 사용할 수 있는 주체를 특정 역할로 한정해야 합니다.
- 키는 주기적으로 교체되어야 합니다.
신규 S3 버킷은 이미 SSE-S3로 자동 암호화되지만, 그 키는 AWS가 소유하므로 키 정책도 사용 기록도 우리가 다룰 수 없습니다. 요구 2와 3이 고객 관리형 키를 지목합니다.
아키텍처 개요
| 키 종류 | 키 정책 편집 | 자동 교체 | 사용 기록 |
|---|---|---|---|
| AWS 소유 키 | 불가 | AWS가 관리 | 계정에서 볼 수 없다 |
| AWS 관리형 키 | 직접 편집 불가 | 매년 자동. 켜고 끌 수 없다 | 남는다 |
| 고객 관리형 키 | 직접 편집 | 켤 수 있고 주기를 조정할 수 있다 | 남는다 |
봉투 암호화의 흐름
대용량 데이터를 KMS로 직접 암호화하지는 않습니다. KMS는 데이터 키를 만들어 주고, 실제 암호화는 그 데이터 키로 로컬에서 합니다.
- KMS에 데이터 키를 요청하면 평문 키와 암호문 키 두 벌을 돌려줍니다.
- 애플리케이션이 평문 데이터 키로 데이터를 암호화합니다.
- 평문 데이터 키를 메모리에서 지우고, 암호문 데이터 키를 데이터와 함께 저장합니다.
- 복호화할 때는 암호문 데이터 키를 KMS에 보내 평문 키를 받아 씁니다.
이 구조 덕분에 대용량 데이터가 KMS를 통과하지 않습니다. KMS가 보는 것은 데이터 키뿐입니다.
구성 단계
1 · 키 정책 작성
키 정책은 키에 붙는 리소스 기반 정책입니다. 키 정책이 계정에 권한 위임을 허용하지 않으면 IAM 정책만으로는 이 키를 쓸 수 없습니다.
{
"Version": "2012-10-17",
"Id": "lab11-key-policy",
"Statement": [
{
"Sid": "EnableIamPolicies",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowKeyAdministration",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:role/SecurityAdminRole" },
"Action": [
"kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*",
"kms:Put*", "kms:Update*", "kms:Revoke*", "kms:Disable*",
"kms:Get*", "kms:Delete*", "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "AllowKeyUsageByApp",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:role/Lab11AppRole" },
"Action": [
"kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*",
"kms:GenerateDataKey*", "kms:DescribeKey"
],
"Resource": "*"
}
]
}
2 · 키 생성과 별칭
KEY_ID=$(aws kms create-key \
--description "lab11 document encryption key" \
--key-usage ENCRYPT_DECRYPT \
--key-spec SYMMETRIC_DEFAULT \
--policy file://key-policy.json \
--query 'KeyMetadata.KeyId' --output text)
aws kms create-alias \
--alias-name alias/lab11-documents \
--target-key-id "$KEY_ID"
aws kms describe-key --key-id alias/lab11-documents \
--query 'KeyMetadata.[KeyId,KeySpec,KeyUsage,KeyManager,Enabled,MultiRegion]' \
--output table
KeyManager가 CUSTOMER로 나오면 고객 관리형 키입니다.
별칭을 쓰면 코드와 정책에서 키 ID를 하드코딩하지 않아도 되고,
나중에 키를 교체할 때 별칭만 옮기면 됩니다.
3 · 자동 교체
aws kms enable-key-rotation --key-id "$KEY_ID"
aws kms get-key-rotation-status --key-id "$KEY_ID" \
--query '[KeyRotationEnabled, RotationPeriodInDays]' --output json
| 항목 | 내용 |
|---|---|
| 기본 주기 | 약 365일. 값을 지정해 조정할 수 있다 |
| AWS 관리형 키 | 매년 자동 교체되며 켜고 끌 수 없다 |
| 적용 대상 | 대칭 암호화 키만. 비대칭·HMAC·가져온 키 재료·사용자 지정 키 스토어는 제외 |
| 기존 데이터 | 다시 암호화되지 않는다. KMS가 이전 키 재료를 보관해 복호화를 계속 지원한다 |
4 · 버킷 기본 암호화
aws s3api put-bucket-encryption \
--bucket example-lab11-docs \
--server-side-encryption-configuration '{
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:ap-northeast-2:123456789012:key/12345678-1234-1234-1234-123456789012"
},
"BucketKeyEnabled": true
}
]
}'
aws s3api get-bucket-encryption --bucket example-lab11-docs
버킷 키를 켜면 객체마다 KMS를 호출하는 대신 버킷 수준의 키를 재사용해 KMS 호출 수가 줄어듭니다. 객체가 많은 버킷에서 비용과 스로틀링 양쪽에 도움이 됩니다.
| 방식 | 키 관리 주체 | 키 사용 감사 | 고르는 신호 |
|---|---|---|---|
| SSE-S3 | AWS | 키 단위로 볼 수 없다 | 기본값. 특별한 요구가 없을 때 |
| SSE-KMS | 고객(고객 관리형 키) 또는 AWS 관리형 키 | 호출 기록이 남는다 | "키 사용을 감사해야 한다", "키 접근을 통제해야 한다" |
| SSE-C | 고객이 요청마다 키 제공 | AWS가 키를 보관하지 않는다 | 키를 AWS에 두지 않아야 할 때 |
| 클라이언트 측 암호화 | 고객 | — | AWS에 도달하기 전에 암호화해야 할 때 |
검증 방법
봉투 암호화를 직접 해 본다
# 1) 데이터 키를 요청하면 평문과 암호문 두 벌이 돌아옵니다.
aws kms generate-data-key \
--key-id alias/lab11-documents \
--key-spec AES_256 \
--query '[Plaintext, CiphertextBlob]' \
--output json > datakey.json
# 2) 평문 데이터 키로 파일을 암호화합니다(실제로는 애플리케이션이 수행).
# 평문 키는 사용 후 즉시 폐기하고, 암호문 키만 데이터와 함께 보관합니다.
# 3) 복호화할 때는 암호문 데이터 키를 KMS 에 보내 평문 키를 받습니다.
aws kms decrypt \
--ciphertext-blob fileb://encrypted-data-key.bin \
--key-id alias/lab11-documents \
--query 'Plaintext' --output text
객체 암호화 확인
aws s3 cp ./contract.pdf s3://example-lab11-docs/
aws s3api head-object \
--bucket example-lab11-docs \
--key contract.pdf \
--query '[ServerSideEncryption, SSEKMSKeyId, BucketKeyEnabled]' \
--output json
| 항목 | 통과 기준 |
|---|---|
| 객체 암호화 | ServerSideEncryption이 aws:kms이고 키 ID가 우리 키다 |
| 버킷 키 | BucketKeyEnabled가 참이다 |
| 권한 분리 | 키 정책에 없는 역할로는 객체를 읽을 수 없다 |
| 사용 기록 | CloudTrail에 GenerateDataKey·Decrypt 호출이 남는다 |
| 교체 | 자동 교체가 활성화되어 있고 주기가 확인된다 |
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue="$KEY_ID" \
--max-results 10 \
--query 'Events[].[EventTime,EventName,Username]' \
--output table
여기서 고객 관리형 키를 쓰는 이유가 눈에 보입니다. 어떤 주체가 언제 이 키로 무엇을 했는지 목록으로 제시할 수 있습니다 (케이스 10).
비용 고려
| 항목 | 성격 |
|---|---|
| 고객 관리형 키 | 키 하나당 보관 항목이 생긴다 |
| KMS API 호출 | 암호화·복호화·데이터 키 생성 호출 수 |
| S3 버킷 키 | KMS 호출 수를 크게 줄인다 |
| AWS 관리형 키 | 키 보관 항목이 없고 호출만 계산된다 |
| 키 개수 | 데이터 분류마다 키를 나누면 통제는 정밀해지고 항목은 늘어난다 |
객체 수가 많은 버킷에서 SSE-KMS를 쓸 때는 버킷 키를 켜는 것이 사실상 필수입니다. 켜지 않으면 객체마다 KMS 호출이 발생해 비용과 요청 한도 양쪽에서 문제가 됩니다.
프로덕션 고려사항
- 키를 데이터 분류 단위로 나눕니다. 모든 데이터를 하나의 키로 암호화하면 폭발 반경이 커지고, 키 정책으로 접근을 나누기도 어렵습니다.
- 키 관리자와 키 사용자를 분리합니다. 키를 지울 수 있는 사람과 데이터를 읽을 수 있는 사람이 같을 이유가 없습니다.
- 삭제는 예약이며 대기 기간이 있습니다. 키를 지우면 그 키로 암호화된 데이터를 영구히 복호화할 수 없습니다. 대기 기간 안에는 취소할 수 있으므로 절차를 문서로 남깁니다.
- 다중 리전 키는 재해 복구 구성에서 유용합니다. 다만 리전 간에 키 재료를 공유하는 것이 규정상 허용되는지 먼저 확인합니다.
- 암호화 컨텍스트를 표준화하면 로그가 훨씬 유용해집니다.
- 교차 계정 사용이 필요하면 키 정책에서 그 계정을 허용하고, 상대 계정에서도 IAM 정책으로 허용해야 합니다. 양쪽이 모두 필요합니다.
- 스냅샷과 AMI의 암호화는 키를 따라갑니다. 다른 리전이나 계정으로 공유하려면 대상 쪽에서 접근할 수 있는 키로 다시 암호화해야 합니다.
정리 — 리소스 삭제
aws s3 rm s3://example-lab11-docs --recursive
aws s3api delete-bucket --bucket example-lab11-docs
aws kms delete-alias --alias-name alias/lab11-documents
# 대기 기간을 두고 삭제를 예약합니다. 기간 안에는 취소할 수 있습니다.
aws kms schedule-key-deletion \
--key-id "$KEY_ID" \
--pending-window-in-days 7 \
--query '[KeyState, DeletionDate]' --output json
# 마음이 바뀌면 취소합니다.
# aws kms cancel-key-deletion --key-id "$KEY_ID"
자주 하는 실수
| 실수 | 증상 | 고치는 법 |
|---|---|---|
| 키 정책에서 계정 위임 문을 뺌 | IAM 정책이 효력을 잃고 키가 잠긴다 | 계정 루트에 대한 위임 문을 유지한다 |
| IAM 정책만 주고 키 정책을 안 봄 | 권한이 있는데도 접근이 거부된다 | 키 정책과 IAM 정책 양쪽을 확인한다 |
| 버킷 키를 끔 | 객체마다 KMS 호출이 발생해 비용과 스로틀링이 늘어난다 | BucketKeyEnabled를 켠다 |
| 비대칭 키에 자동 교체를 기대 | 교체가 적용되지 않는다 | 대칭 암호화 키만 대상임을 확인한다 |
| 교체하면 기존 데이터가 다시 암호화된다고 오해 | 규정 대응에서 잘못된 설명을 하게 된다 | 기존 데이터는 이전 키 재료로 계속 복호화된다 |
| 데이터 정리 없이 키 삭제 예약 | 대기 기간이 지나면 데이터를 영영 읽을 수 없다 | 객체를 먼저 정리하거나 다른 키로 재암호화한다 |
| 교차 계정에서 한쪽만 허용 | 접근이 거부된다 | 키 정책과 상대 계정 IAM 정책을 모두 설정한다 |
이어서 볼 곳
- 16장 · 보안 · 암호화 · 거버넌스 KMS 키 종류와 키 정책, 봉투 암호화, Secrets Manager와 Parameter Store를 다룹니다.
- 8장 · S3와 오브젝트 스토리지 암호화 4종의 키 관리 주체와 감사 가능성 차이를 다룹니다.
- 케이스 10 · 규정 준수 감사에 대응하라 키 사용 기록이 감사 질문에 어떻게 답이 되는지 설계 관점에서 다룹니다.
- 케이스 1 · EC2에 액세스 키를 심어 배포했다 키 관리와 자격 증명 관리는 다른 문제입니다. 두 축을 구분해 봅니다.
- 실습 3 · S3 정적 사이트 + CloudFront + OAC 고객 관리형 키로 암호화한 오리진을 CloudFront가 읽을 때의 권한을 확인합니다.
공식 문서 출처
- AWS KMS API 레퍼런스 — CreateKey
— 키 사양(
SYMMETRIC_DEFAULT등)과 키 용도, 키 정책 지정 - AWS KMS API 레퍼런스 — EnableKeyRotation — 기본 교체 주기 365일, 대칭 암호화 키에만 적용, AWS 관리형 키의 매년 자동 교체
- AWS KMS API 레퍼런스 — GenerateDataKey — 평문 키와 암호문 키를 함께 반환하는 봉투 암호화 동작, 암호화 컨텍스트
- AWS KMS API 레퍼런스 — PutKeyPolicy — 키 정책이 키 접근을 통제하는 방식과 정책 잠금 검사
- AWS KMS API 레퍼런스 — ScheduleKeyDeletion — 삭제 예약과 대기 기간, 취소 가능성
- Amazon S3 API 레퍼런스 — PutBucketEncryption
—
SSEAlgorithm값과KMSMasterKeyID,BucketKeyEnabled - Amazon S3 사용 설명서 — SSE-KMS로 데이터 보호 — 버킷 키가 KMS 호출을 줄이는 방식