[AWS-Security] 7.3. 저장 데이터 보안
1. 저장 데이터 보안 및 암호화
저장 데이터란 Amazon S3, DynamoDB, EBS 등 디스크에 기록되어 보관 중인 데이터를 말한다. 이 저장 데이터의 기밀성을 보호하는 가장 확실한 방법은 데이터 암호화이다.
1.1. AWS KMS (Key Management Service)
AWS KMS는 AWS 환경 전체에서 암호화 키를 쉽게 생성하고 관리할 수 있게 해주는 중앙 집중식 서비스이다.
타사 API 키와 같은 민감한 정보를 소스 코드에 평문으로 저장하지 않고, KMS로 암호화하여 저장하는 과정은 아래와 같다.
- 고객 마스터 키(CMK) 생성 : KMS에서 데이터를 암호화하려면 마스터 키(CMK, Customer Master Key)가 필요하다.
1 2 3
# 고객 마스터 키(CMK) 생성 $ aws kms create-key # 결과로 암호화/복호화에 모두 사용되는 대칭 키(Symmetric Key)의 KeyId가 반환됨
- 데이터 암호화 (Encrypt) : 생성한 CMK를 사용하여 평문(Plaintext) API 키를 암호화 한다.
1 2 3 4 5
# 평문 문자열을 암호화하는 KMS 명령어 $ aws kms encrypt \ --key-id 1234abcd-12ab-34cd-56ef-1234567890ab \ --plaintext "MyApiKey" # 결과로 길고 복잡한 Base64 인코딩 형태의 암호문인 'CiphertextBlob'이 반환됨
- 데이터 복호화 (Decrypt) : 애플리케이션이 API 키를 사용해야 할때, 암호화된 블롭(Blob)을 다시 평문으로 되돌린다. 대칭 키를 사용했으므로 복호화 시 KMS가 알아서 CMK를 식별한다.
1 2 3 4
# 암호문 블롭을 복호화하는 KMS 명령어 $ aws kms decrypt \ --ciphertext-blob fileb://<(echo "AQICAHh..." | base64 -d) # 결과로 원래의 API 키(MyApiKey) 평문이 반환됨
2.2. 서버 측 암호화 (SSE, Server-Side Encryption) 자동 적용
수동으로 하나씩 암호화하는 대신, AWS 서비스(S3, DynamoDB 등) 자체에서 데이터가 저장될 때 자동으로 암호화되도록 설정할 수 있다. 이를 서버 측 암호화(SSE)라고 한다.
2.2.1. Amazon S3의 기본 암호화 설정
S3 Bucket에는 업로드되는 모든 새 객체가 자동으로 암호화되도록 버킷 정책을 구성할 수 있다.
- 키 선택 : S3에서 자체 관리하는 키(SSE-S3, AES-256)를 사용할 수도 있고, 유연한 권한 제어를 위해 KMS 키(SSE-KMS)를 선택할 수도 있다.
1 2 3 4
# S3 버킷에 대한 기본 암호화(AES-256)를 활성화하는 명령어 $ aws s3api put-bucket-encryption \ --bucket my-bucket \ --server-side-encryption-configuration '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}'
이 설정을 켜더라도 설정 변경 이후에 추가된 객체만 암호화된다. 기존에 들어있던 객체들은 자동으로 암호화되지 않는다.
- IAM 권한 : 만약 SSE-KMS(KMS 키 사용) 방식으로 S3를 암호화 했다면, 해당 버킷의 데이터를 읽고 쓰려는 IAM 사용자나 역할은 반드시 KMS 정책 상의
kms:Encrypt및kms:Decrypt권한을 함께 가지고 있어야만 정상적으로 접근할 수 있다.
2.2.2. Amazon DynamoDB의 암호화 설정
DynamoDB 테이블을 생성할 때 속성 값 하나만 추가하면 모든 데이터가 KMS를 통해 자동으로 암호화된다.
1
2
3
4
5
# SSE-KMS로 신규 DynamoDB 테이블 생성
$ aws dynamodb create-table \
--table-name Music \
...
--sse-specification Enabled=true,SSEType=KMS # 저장 데이터 암호화를 활성화
3. 최소 접근 권한 제어
3.1. 리소스 기반 정책을 활용한 강력한 접근 제어
3.1.1. 개요
데이터 스토리지를 보호할 때는 사용자에게 권한을 주는 ‘ID 기반 정책’보다, 스토리지 자체에 문지기를 세우는 ‘리소스 기반 정책(버킷 정책)’이 훨씬 강력하고 적합하다.
3.1.2. 앨리스(Alice)만 데이터를 수정할 수 있게 만들기
- ID 기반 정책의 한계 : 앨리스의 IAM 계정에 쓰기 권한을 줄 수는 있지만, 다른 관리자가 밥(Bob)에게도 쓰기 권한을 줘버리는 것을 막을 수는 없다.
- 리소스 기반 정책(버킷 정책)의 해결책 : 버킷 정책에 앨리스가 아니면 모두 거부(Deny)라는 규칙을 넣으면 다른 누군가가 권한을 얻더라도 원천 차단된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 앨리스를 제외한 모든 사용자에 대한 접근을 거부하는 S3 버킷 정책 { "Version": "2012-10-17", "Statement": [{ "Effect": "Deny", "NotPrincipal": { // "다음 지정된 사용자가 '아닌' 경우"를 의미 "AWS": [ "arn:aws:iam::123456789012:user/Alice" ] }, "Action": "s3:*", // 모든 S3 작업에 대해 "Resource": [ "arn:aws:s3:::mybucket", "arn:aws:s3:::mybucket/*" ] }] }
- 핵심 :
NotPrincipal요소와 Deny 효과를 결합하여, 명시된 주체(Alice)를 제외한 모든 사람의 접근을 완벽하게 차단한다. (방화벽의 화이트리스트와 유사)
3.2. Amazon S3 접근 제어 목록 (ACL)
IAM 정책 외에도 S3는 ACL이라는 또 다른 레거시 접근 제어 메커니즘을 제공한다. 자체적으로 정교한 ACL을 작성할 수도 있지만, S3에서 미리 만들어둔 표준화된 ACL을 사용하는 경우가 많다.
| 표준화된 ACL 이름 | 적용 대상 | 설명 (관련 권한) |
|---|---|---|
| private | 버킷/객체 | (기본값) 소유자만 모든 권한을 가진다. 외부 접근은 완벽히 차단된다. |
| public-read | 버킷/객체 | 소유자는 모든 권한을, 익명 사용자를 포함한 전 세계 누구나 ‘읽기’가 가능하다. (예: 퍼블릭 웹사이트 이미지 호스팅) |
| public-read-write | 버킷/객체 | 누구나 읽고 쓸 수 있다. (보안상 매우 위험하므로 절대 사용 지양) |
| aws-exec-read | 버킷/객체 | Amazon EC2가 버킷에서 AMI 번들을 가져올 수 있도록 읽기 권한을 부여한다. |
| authenticated-read | 버킷/객체 | 익명 사용자는 불가하며, 인증을 거친 모든 AWS 사용자에게 읽기 권한을 부여한다. |
| bucket-owner-read | 객체 전용 | (객체 소유자와 버킷 소유자가 다를 때) 버킷 소유자에게도 해당 객체를 읽을 권한을 준다. |
| bucket-owner-full-control | 객체 전용 | 버킷 소유자에게도 해당 객체에 대한 모든 권한(수정/삭제 등)을 준다. |
4. 백업 및 버전 관리
데이터 보안에서 가장 중요한 요소 중 하나는 데이터 손실이나 손상에 대한 복원력(Resilience)을 갖추는 것이다.
4.1. Amazon DynamoDB 백업 및 복구
DynamoDB는 데이터베이스 테이블을 백업하고 복구하는 두 가지 강력한 방식을 제공한다.
- 온디맨드 백업 (수동 백업) : 원할 때마다 전체 테이블의 스냅샷을 생성하는 방식이다.
1 2 3 4 5 6 7 8 9
# DynamoDB 테이블에 대한 백업을 생성하는 명령어 $ aws dynamodb create-backup \ --table-name Music \ # 원본 테이블 이름 --backup-name MusicBackup # 생성할 백업의 이름 지정 # 기존 백업에서 DynamoDB 테이블을 복원하는 명령어 $ aws dynamodb restore-table-from-backup \ --target-table-name BackupTable \ # 복원될 '새로운' 테이블 이름 지정 --backup-arn MusicBackup # create-backup 응답에서 받은 백업의 ARN
- 특정 시점 복구 (PITR, Point-In-Time Recovery) : 테이블의 증분(incremental) 백업을 자동으로 수행하여, 과거 35일 내의 원하는 정확한 시점(초 단위)으로 테이블을 되돌릴 수 있는 기능이다.
1 2 3 4 5 6 7 8 9 10
# DynamoDB 테이블에 대해 PITR 기능을 활성화하는 명령어 $ aws dynamodb update-continuous-backups \ --table-name Music \ --point-in-time-recovery-specification PointInTimeRecoveryEnabled=True # PITR 백업에서 DynamoDB 테이블을 복원하는 명령어 $ aws dynamodb restore-table-to-point-in-time \ --source-table-name Music \ --target-table-name MusicEarliestRestorableDateTime \ # 연속 백업은 항상 '신규 테이블'로 복원 --restore-date-time 1519257118.0 # 복원할 정확한 시점(Unix 타임스탬프)
DynamoDB에서 백업을 복원할 때는 기존 테이블을 덮어쓰는 것이 아닌, 완전히 새로운 이름의 신규 테이블로 복원된다.
4.2. Amazon S3 버전 관리 (Versioning)
S3는 별도의 백업 명령을 내리는 대신, 버전 관리 기능을 켜서 객체의 모든 변경 이력을 쌓는 방식을 사용한다.
- 특정 객체의 과거 버전 ID 찾기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# S3 객체의 버전을 가져오는 명령어 (1단계) $ aws s3api list-object-versions \ --bucket my-bucket \ --prefix index.html # 찾고자 하는 객체의 키(이름) # 응답(Response) 예시 { "Versions": [ { "LastModified": "2020-10-10T00:00:00.000Z", "VersionId": "Rb_12T8UHDkFEwCgJjhlgPOZC0qJ.vpD", # 나중에 복원할 때 사용할 고유 버전 ID "IsLatest": false # 최신 버전인지 여부 (false면 과거 버전) }, ... ] }
- 특정 버전 ID를 지정하여 과거 데이터 가져오기 : 그냥
get-object를 호출하면 항상 최신 버전을 가져오지만, 위에서 찾은 VersionId를 파라미터로 지정하면 과거의 특정 상태를 가져올 수 있다.1 2 3 4 5
# S3 객체의 버전을 가져오는 명령어 (2단계) $ aws s3api get-object \ --bucket my-bucket \ --key index.html \ --version-id "Rb_12T8UHDkFEwCgJjhlgPOZC0qJ.vpD" # list-object-versions에서 찾은 과거 버전 ID