학습 목표

시나리오

규제 대상 문서를 S3 버킷에 보관합니다. 요구는 이렇습니다.

신규 S3 버킷은 이미 SSE-S3로 자동 암호화되지만, 그 키는 AWS가 소유하므로 키 정책도 사용 기록도 우리가 다룰 수 없습니다. 요구 2와 3이 고객 관리형 키를 지목합니다.

아키텍처 개요

KMS 키 종류와 통제 범위
키 종류 키 정책 편집 자동 교체 사용 기록
AWS 소유 키 불가 AWS가 관리 계정에서 볼 수 없다
AWS 관리형 키 직접 편집 불가 매년 자동. 켜고 끌 수 없다 남는다
고객 관리형 키 직접 편집 켤 수 있고 주기를 조정할 수 있다 남는다

봉투 암호화의 흐름

대용량 데이터를 KMS로 직접 암호화하지는 않습니다. KMS는 데이터 키를 만들어 주고, 실제 암호화는 그 데이터 키로 로컬에서 합니다.

  1. KMS에 데이터 키를 요청하면 평문 키와 암호문 키 두 벌을 돌려줍니다.
  2. 애플리케이션이 평문 데이터 키로 데이터를 암호화합니다.
  3. 평문 데이터 키를 메모리에서 지우고, 암호문 데이터 키를 데이터와 함께 저장합니다.
  4. 복호화할 때는 암호문 데이터 키를 KMS에 보내 평문 키를 받아 씁니다.

이 구조 덕분에 대용량 데이터가 KMS를 통과하지 않습니다. KMS가 보는 것은 데이터 키뿐입니다.

구성 단계

1 · 키 정책 작성

키 정책은 키에 붙는 리소스 기반 정책입니다. 키 정책이 계정에 권한 위임을 허용하지 않으면 IAM 정책만으로는 이 키를 쓸 수 없습니다.

key-policy.json — 관리자와 사용자를 분리한 키 정책 (계정 ID와 역할 이름은 예시입니다)
{
  "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 · 키 생성과 별칭

create-key.sh — 대칭 암호화 키 생성과 별칭 부여
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 · 자동 교체

enable-rotation.sh — 자동 교체 활성화와 상태 확인
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 · 버킷 기본 암호화

bucket-encryption.sh — SSE-KMS와 버킷 키 활성화 (버킷 이름과 키 ARN은 예시입니다)
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 호출 수가 줄어듭니다. 객체가 많은 버킷에서 비용과 스로틀링 양쪽에 도움이 됩니다.

S3 서버 측 암호화 방식 비교
방식 키 관리 주체 키 사용 감사 고르는 신호
SSE-S3 AWS 키 단위로 볼 수 없다 기본값. 특별한 요구가 없을 때
SSE-KMS 고객(고객 관리형 키) 또는 AWS 관리형 키 호출 기록이 남는다 "키 사용을 감사해야 한다", "키 접근을 통제해야 한다"
SSE-C 고객이 요청마다 키 제공 AWS가 키를 보관하지 않는다 키를 AWS에 두지 않아야 할 때
클라이언트 측 암호화 고객 — AWS에 도달하기 전에 암호화해야 할 때

검증 방법

봉투 암호화를 직접 해 본다

envelope-demo.sh — 데이터 키를 받아 로컬에서 암호화하고 복호화
# 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

객체 암호화 확인

verify-object.sh — 업로드한 객체의 암호화 정보
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 호출이 남는다
교체 자동 교체가 활성화되어 있고 주기가 확인된다
check-usage.sh — 키 사용 기록 확인
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 호출이 발생해 비용과 요청 한도 양쪽에서 문제가 됩니다.

프로덕션 고려사항

정리 — 리소스 삭제

cleanup.sh — 객체 정리 후 키 삭제 예약
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 정책을 모두 설정한다

이어서 볼 곳

공식 문서 출처