Amazon SAA-C03: 보안, IAM, KMS 및 거버넌스 — 학습 가이드
다음의 일부입니다: AWS SAA-C03 — 완벽 학습 가이드. 검증된 답안으로 연습하기: Amazon 시험 허브, 또는 다음에서 시간 제한 모의고사 풀기: ExamRoll.io.
자격 증명 기본: 사용자, 루트, 그룹 및 역할
Identity and Access Management는 모든 워크로드가 통과하는 컨트롤 플레인이며, 그 기본 원칙은 최소 권한입니다. 즉, 필요한 권한만, 필요한 기간 동안만 부여하고, 정적 보안 암호를 보유하는 자격 증명보다는 단기 자격 증명을 생성하는 자격 증명을 선호하는 것입니다.
루트 사용자는 계정을 소유하고 무제한 권한을 가지며, IAM 정책이나 SCP로 제한할 수 없습니다. 이 때문에 루트 사용자는 환경에서 가장 민감한 단일 자격 증명이며, 비상시에만 사용해야 합니다. 새 계정에서는 루트에 하드웨어 또는 가상 MFA 디바이스를 활성화하고, 길고 고유한 암호를 설정하고, 과거의 루트 액세스 키를 모두 제거하고, 복구가 가능하도록 대체 결제/운영/보안 연락처를 등록해야 합니다. 초기 설정(IAM 관리자 자격 증명 생성, 결제 구성, 계정 별칭 설정) 후에는 AWS가 명시적으로 요구하는 일부 작업(계정 폐쇄, 계정 이름 변경, 삭제된 IAM 권한 복원, MFA Delete 활성화, 소수의 S3/CloudFront 루트 서명 작업)을 제외하고는 루트를 다시 사용하지 않습니다. 일상적인 작업에 루트를 사용하는 것은 정책으로 범위를 지정할 수 없고, 공유 시 CloudTrail에서 활동을 추적하기 어려우며, 단 한 번의 침해로 돌이킬 수 없는 통제권을 넘겨주기 때문에 잘못된 방식입니다.
인간의 일상적인 액세스는 최소 권한 정책으로 범위가 지정된 IAM 자격 증명을 통해 이루어집니다. 관리형 정책은 개별 사용자가 아닌 그룹에 연결해야 합니다. AdministratorAccess 정책이 연결된 Administrators 그룹을 만들고 명명된 사용자를 이 그룹에 배치하면, 변경 지점이 단일화되고 모든 사용자에게 동일한 정책을 복사해 붙여넣는 안티패턴을 피할 수 있습니다. 리소스 범위는 * 대신 명시적인 ARN으로 지정해야 합니다.
역할은 전혀 다른 목적을 가집니다. 역할은 서비스, EC2 인스턴스, Lambda 함수, 페더레이션 사용자, 교차 계정 호출자와 같은 보안 주체(principal)에 의해 수임(assume)되며, 자동으로 교체되는 임시 STS 자격 증명을 생성합니다. 이러한 자격 증명은 Git 커밋에 유출될 수 없고 수동으로 교체할 때까지 지속되는 대신 몇 분에서 몇 시간 내에 만료되므로, 역할은 인간이 아닌 모든 것에 대한 기본 자격 증명입니다.
역할당 두 개의 정책: 권한과 신뢰
모든 역할은 두 개의 독립적인 문서에 의해 관리되며, 둘 중 하나를 잊는 것은 전형적인 실패 원인입니다.
권한 정책(자격 증명 기반)은 역할이 수임된 후 무엇을 할 수 있는지를 선언합니다. 신뢰 정책(리소스 기반, 역할 자체에 연결됨)은 누가 그 역할을 수임할 수 있는지를 선언합니다. 역할에 AmazonS3ReadOnlyAccess를 연결하더라도 어떤 보안 주체도 해당 역할에 대해 sts:AssumeRole을 호출할 수 없다면 아무 소용이 없습니다. 반대로, 권한 정책이 없는 허용적인 신뢰 정책은 수임은 가능하지만 유용한 작업을 수행할 수 없는 역할을 만듭니다.
Lambda 실행 역할은 서비스 보안 주체 패턴을 보여줍니다. 즉, 계정 소유자가 아닌 서비스 자체가 호출자입니다.
AssumeRolePolicyDocument: # trust policy
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: lambda.amazonaws.com }
Action: sts:AssumeRole
Policies: # permissions
- PolicyName: ReadOrders
PolicyDocument:
Statement:
- Effect: Allow
Action: dynamodb:GetItem
Resource: arn:aws:dynamodb:*:*:table/Orders
워크로드 자격 증명: 인스턴스 프로파일, 태스크 역할, IRSA, Roles Anywhere
템플릿, 환경 변수 또는 노트북에 존재하는 모든 자격 증명은 미래의 보안 침해 사고로 이어질 수 있습니다. 표준적인 AWS 패턴은 정적 액세스 키를 역할(role)을 통해 전달되는 단기 수명의 자동 교체 자격 증명으로 대체하는 것입니다.
EC2의 경우, 전달 메커니즘은 인스턴스 프로파일입니다. 이는 IAM 역할을 인스턴스에 바인딩하는 얇은 컨테이너로, 인스턴스 메타데이터 서비스(IMDSv2)가 SDK에 임시 자격 증명을 제공할 수 있도록 합니다. 기본 자격 증명 공급자 체인이 별도 설정 없이 이를 찾으므로 boto3.client('s3')는 그냥 작동하고 애플리케이션 코드는 자격 증명을 볼 필요가 없습니다. SSRF 기반 자격 증명 유출을 막기 위해 IMDSv2(HttpTokens: required)를 강제해야 합니다. 자격 증명은 대략 6시간마다 교체되며, 폐기는 간단한 역할 편집으로 가능합니다.
AppRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal: { Service: ec2.amazonaws.com }
Action: sts:AssumeRole
Policies:
- PolicyName: S3DocAccess
PolicyDocument:
Statement:
- Effect: Allow
Action: [s3:GetObject, s3:PutObject]
Resource: arn:aws:s3:::docs-bucket/*
AppInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles: [!Ref AppRole]
ECS에서는 **태스크 역할(task role)**이, Lambda에서는 **실행 역할(execution role)**이, EKS 파드에서는 IRSA (IAM Roles for Service Accounts) 또는 EKS Pod Identity가 유사한 역할을 합니다. 모든 경우에 AWS 자체가 역할을 기반으로 자격 증명을 중개하며 워크로드는 장기 보안 암호를 절대 보지 않습니다.
액세스 키를 AMI, 사용자 데이터 스크립트 또는 .env 파일에 포함시키는 것은 세 가지 구체적인 이유로 잘못되었습니다. 키는 자동으로 교체되지 않으며, 소스 VPC 엔드포인트와 같은 세션 컨텍스트로 범위를 지정할 수 없으며, 인스턴스가 침해되거나 AMI가 실수로 공유되면 자격 증명이 영구적으로 유출됩니다.
온프레미스 서버, 다른 클라우드, CI 러너 등 AWS 외부의 워크로드가 내장된 키 없이 임시 AWS 자격 증명이 필요한 경우, IAM Roles Anywhere는 프라이빗 CA(AWS Private CA 또는 자체 CA)의 X.509 인증서를 신뢰 앵커(trust anchor)로 사용합니다. 워크로드는 클라이언트 인증서를 제시하고 단기 STS 자격 증명을 받습니다.
aws_signing_helper credential-process \
--certificate /etc/pki/client.pem \
--private-key /etc/pki/client.key \
--trust-anchor-arn arn:aws:rolesanywhere:...:trust-anchor/... \
--profile-arn arn:aws:rolesanywhere:...:profile/... \
--role-arn arn:aws:iam::111122223333:role/OnPremWorkload
이는 인스턴스 역할과 Secrets Manager가 AWS 내부에서 해결하는 것과 동일한 안티패턴을 해결합니다.
대규모 인력 액세스: Identity Center, SAML, Directory Service
어느 정도 규모 이상에서는 계정별로 IAM 사용자를 프로비저닝하는 것이 관리하기 어렵습니다. AWS IAM Identity Center(AWS SSO의 후속 서비스)는 직원 액세스를 위한 권장 진입점입니다. 이는 Organization의 모든 계정으로 페더레이션되는 단일 디렉토리이며, 권한 세트(permission sets)(IdP 그룹에 매핑된 템플릿화된 IAM 역할)를 통해 임시 역할 기반 세션을 발급합니다. 사용자는 Identity Center 포털에서 한 번 인증한 다음, 할당된 모든 계정으로 권한 세트를 수임합니다.
Identity Center는 SAML 2.0 및 SCIM을 통해 외부 IdP(Okta, Entra ID/Azure AD, Google Workspace, ADFS)와 통합하여 입사/이동/퇴사(joiner/mover/leaver) 흐름을 자동화하고, AWS Directory Service AD Connector(프록시) 또는 AWS Managed Microsoft AD(AWS 내의 전체 복제본)를 통해 온프레미스 Active Directory와 통합됩니다. 인증되지 않았거나 서드파티 인증 사용자의 모바일 또는 웹 앱에서 AWS를 호출해야 하는 경우, Amazon Cognito는 외부 자격 증명을 임시 STS 자격 증명으로 교환하여 내장된 장기 키를 사용하지 않도록 합니다.
계정 간 액세스
계정 간 액세스는 사용자를 공유하는 방식이 아닌 역할을 통해 이루어지며, 양쪽 모두의 동의가 필요합니다. 대상 계정(B)은 신뢰 정책에 계정 A(또는 그 안의 특정 보안 주체)를 지정하는 역할을 생성해야 하며, 계정 A의 호출자 또한 해당 역할의 ARN을 대상으로 하는 sts:AssumeRole 권한을 가지고 있어야 합니다. 한쪽만으로는 충분하지 않습니다. 이 방식은 수명이 짧은 자격 증명을 생성하며, 양쪽 계정 모두에 명확한 CloudTrail 감사 추적을 남깁니다.
제3자(SaaS 공급업체)가 역할을 수임하는 보안 주체일 경우, ExternalId 조건을 추가하여 혼동된 대리인 문제를 방지하고, MFA 사용을 요구하는 것을 고려해야 합니다:
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::222222222222:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "a1b2c3-unique-token" },
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
}
서비스 간 계정 교차 호출의 경우, 리소스 정책이 그 역할을 합니다. 계정 A의 SNS 주제가 계정 B의 Lambda를 호출하도록 하려면:
aws lambda add-permission \
--function-name ProcessNotification \
--statement-id AllowSNSInvoke \
--action lambda:InvokeFunction \
--principal sns.amazonaws.com \
--source-arn arn:aws:sns:us-east-1:111111111111:my-topic
--principal sns.amazonaws.com은 서비스 보안 주체(SNS 자체가 Lambda를 호출함)이며, --source-arn은 특정 주제로 신뢰 범위를 한정하여 혼동된 대리인 문제를 방지합니다.
조직(Organization) 내 계정 간 S3 공유 시, 버킷 정책에 모든 계정 ARN을 나열하는 순진한 접근 방식은 확장성이 떨어지며 새 계정이 추가될 때마다 깨집니다. 올바른 패턴은 aws:PrincipalOrgID를 사용하는 것입니다:
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::reports-bucket/*",
"Condition": {
"StringEquals": { "aws:PrincipalOrgID": "o-abcd1234ef" }
}
}
해당 조직 내 모든 계정의 모든 보안 주체는 허용되고, 그 외에는 모두 거부됩니다. 조건 없이 Principal: "*"를 사용하면 버킷이 퍼블릭 상태가 된다는 점에 유의하세요. 바로 이 조건 키가 범위를 제한하는 역할을 합니다. 자격 증명 측면의 정책도 여전히 중요합니다. 계정 간 액세스는 양쪽 모두에서 호출을 허용해야 하므로, 멤버 계정의 사용자들은 (계정 루트가 아닌 한) 자신의 IAM 정책에 의해 s3:GetObject 권한을 부여받아야 합니다.
특히 S3의 경우, 2023년부터 기본 객체 소유권 설정인 **버킷 소유자 강제 적용(Bucket owner enforced)**이 ACL을 완전히 비활성화하여, 버킷 정책이 버킷에 대한 유일한 권한 부여 메커니즘이 되었습니다. 이전에 다른 계정에서 객체를 업로드한 경우, 기존 객체 ACL이나 bucket-owner-full-control 미리 준비된 ACL(canned ACL)이 여전히 적용될 수 있습니다.
Organizations 및 서비스 제어 정책
AWS Organizations는 계정들을 OU(조직 단위) 트리 구조로 통합하며, 루트에는 관리 계정이 위치합니다. **서비스 제어 정책(Service Control Policies)**은 루트, OU 또는 개별 계정에 연결되는 가드레일입니다. 이 정책은 계정 내 모든 IAM 사용자와 역할에 적용되며, 계정 루트를 포함합니다. 하지만 관리 계정 자체에는 적용되지 않으므로, 관리 계정에서는 워크로드를 실행해서는 안 됩니다.
핵심적인 사고 모델: SCP는 절대로 권한을 부여하지 않습니다. SCP는 계정에서 허용되는 작업의 최대 범위를 정의합니다. 어떤 작업은 자격 증명 또는 리소스 정책에 의해 허용되고 또한 해당 계정의 경로에 있는 어떤 SCP에 의해서도 차단되지 않은 경우에만 허용됩니다. 개발자가 AdministratorAccess 권한을 가지고 있더라도 SCP가 ap-southeast-2 리전 외부에서의 ec2:RunInstances를 거부하면, us-east-1에서의 인스턴스 시작은 실패합니다. 반대로, s3:*를 허용하는 SCP 자체만으로는 아무런 효과가 없습니다. 사용자는 여전히 s3:*를 부여하는 IAM 정책이 필요합니다. SCP는 IAM이 이미 허용한 것을 필터링하는 역할을 합니다. 즉, 권한의 하한선(floor)이 아니라 상한선(ceiling)입니다.
일반적인 SCP 사용 사례로는 리전 잠금, CloudTrail 또는 GuardDuty 비활성화 금지, 비상용 역할(break-glass role) 외의 KMS 키 삭제 금지, 암호화 강제 등이 있습니다. EBS 볼륨 시작 시 암호화를 강제하려면 두 가지 메커니즘을 결합합니다. 리전에서 **기본적으로 EBS 암호화(EBS encryption by default)**를 활성화하고(사용자가 스크립트를 변경하지 않도록 하는 리전별 계정 설정), 감사 가능한 가드레일로서 SCP를 추가합니다:
{
"Effect": "Deny",
"Action": "ec2:RunInstances",
"Resource": "arn:aws:ec2:*:*:volume/*",
"Condition": { "Bool": { "ec2:Encrypted": "false" } }
}
SCP는 전체 계정에 적용되므로, 멤버 계정의 관리자 권한이 탈취되더라도 우회할 수 없습니다. **태그 정책(Tag policies)**은 비용 할당 및 ABAC가 안정적으로 작동하도록 표준화된 태그 키와 대소문자(costcenter가 아닌 CostCenter)를 강제합니다. Organizations는 위임된 관리자(delegated administration) 기능도 지원합니다. 보안 서비스를 관리 계정에서 실행하는 대신, 멤버 계정(“보안” 또는 “감사” 계정)을 GuardDuty, Security Hub, IAM Access Analyzer, Config의 관리자로 위임하여 직무 분리를 유지할 수 있습니다.
KMS: 키, 키 정책, 그리고 소유권 모델
KMS는 소유권과 제어 권한에 따라 키 물질을 구분합니다:
| 모델 | 키 물질 | 교체 | 감사 가능 | 사용 사례 |
|---|---|---|---|---|
| SSE-S3 / AWS 소유 | AWS, 숨겨짐 | 자동, 불투명 | 보이지 않음 | 간단한 “저장 시 암호화” |
AWS 관리형 CMK (aws/service) | AWS | 매년 자동 | 예 | 기본, 제어 필요 없음 |
| 고객 관리형 CMK | AWS KMS, 사용자가 정책 소유 | 선택적 연간(활성화 필요), 90–2560일로 구성 가능 | 예 | 비활성화, 감사, 범위 지정 또는 공유가 필요한 경우 |
| 가져온 키 물질 | 사용자가 생성하여 KMS로 가져옴 | 수동으로 다시 가져옴; 자동 교체 안 됨 | 예 | 규제상 키를 직접 생성해야 하는 경우 |
| 외부 키 스토어(XKS) | XKS 프록시를 통한 온프레미스 HSM | 외부에서 제어 | 예 | 데이터 주권; 키가 온프레미스를 벗어나지 않음 |
| SSE-C | 요청별로 고객이 제공 | 수동 | 제한적 | 클라이언트가 키 물질 보유를 고집하는 경우 |
CMK는 키에 연결된 리소스 기반 정책인 키 정책에 의해 관리됩니다. 거의 모든 다른 AWS 리소스와 달리, 키 정책이 먼저 IAM에 권한을 위임하지 않으면 IAM 정책만으로는 KMS 키에 대한 액세스 권한을 부여할 수 없습니다:
{
"Sid": "EnableIAMPolicies",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:root" },
"Action": "kms:*",
"Resource": "*"
}
이 구문이 없으면 어떤 IAM 정책으로도 키를 사용할 수 없습니다. 키 정책과 호출자의 IAM 정책 모두 해당 작업을 허용해야 합니다. 이것이 가장 빈번하게 발생하는 암호화 오류입니다. IAM 정책에서 kms:Decrypt를 부여하는 것은 필요하지만 충분하지 않습니다. 만약 키 정책이 IAM에 권한을 위임하지 않거나 해당 보안 주체를 명시하지 않으면, 관리자라 할지라도 AccessDenied 오류와 함께 복호화에 실패합니다.
사용자를 대신하여 KMS를 사용하는 서비스(EBS, S3, RDS, Lambda)의 경우, 호출하는 역할에는 일반적으로 kms:GenerateDataKey, kms:Decrypt, 그리고 종종 kms:CreateGrant 권한이 필요합니다. CMK를 사용하여 EBS 볼륨을 암호화하는 EKS 관리형 노드 그룹의 경우, Auto Scaling 서비스 연결 역할(service-linked role)이 키 정책에 kms:CreateGrant 권한과 함께 명시되어야 합니다. 그렇지 않으면 인스턴스 시작이 조용히 실패합니다.
고객 관리형 CMK의 키 교체는 명시적으로 활성화해야 합니다. 많은 실무자들이 모든 KMS 키가 자동으로 교체된다고 잘못 가정합니다:
aws kms enable-key-rotation --key-id alias/my-cmk
aws kms get-key-rotation-status --key-id alias/my-cmk
키 교체를 해도 동일한 키 ID와 별칭은 유지됩니다. 백업 키 물질은 변경되지만, KMS가 기존 암호문을 복호화하기 위해 이전 키 물질을 보관하고 새로운 쓰기 작업에는 새 키 물질을 사용하므로 과거의 암호문도 여전히 복호화할 수 있습니다. 가져온 키 물질은 절대 자동으로 교체되지 않습니다. KMS는 7일에서 30일 사이의 필수적인 삭제 대기 기간을 강제합니다. 이를 CloudTrail에서 ScheduleKeyDeletion 또는 DisableKey와 일치하는 EventBridge 규칙과 결합하고 SNS 주제를 대상으로 지정하면, 폴링 없이 서버리스 방식으로 알림 패턴을 구현할 수 있습니다:
{
"source": ["aws.kms"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": { "eventName": ["ScheduleKeyDeletion", "DisableKey"] }
}
**외부 키 스토어(External Key Stores)**는 규제 기관이 키 물질이 고객이 제어하는 HSM 내에 물리적으로 존재하도록 요구할 때 모델을 확장합니다. KMS는 온프레미스 HSM과 통신하는 XKS 프록시로 암호화 작업을 전달합니다. 만약 HSM이 오프라인 상태이면 복호화가 실패하며, 가용성은 고객의 책임이 됩니다.
**다중 리전 키(Multi-Region Keys, MRK)**는 여러 리전에 걸쳐 동일한 키 ID와 키 물질을 공유하므로, us-east-1에서 생성된 암호문을 eu-west-1에서 직접 복호화할 수 있습니다. 이는 DynamoDB 글로벌 테이블, SSE-KMS를 사용하는 S3 교차 리전 복제, 그리고 대기 리전에서 암호화된 백업을 읽어야 하는 DR(재해 복구) 시나리오에 적합한 패턴입니다. 표준 단일 리전 키를 사용하면 복제 시점에 복호화 후 다시 암호화하는 과정이 필요합니다.
계정 간 암호화된 리소스 공유
암호화된 AMI 및 EBS 스냅샷 공유는 가장 흔한 계정 간 실패 유형 중 하나입니다. 왜냐하면 네 가지 조정된 작업이 필요하기 때문입니다: (1) 고객 관리형 CMK를 사용해야 합니다(AWS 관리형 키는 공유할 수 없습니다). (2) 대상 계정을 키 정책의 보안 주체로 추가하고 kms:Decrypt, kms:DescribeKey, kms:CreateGrant, kms:ReEncrypt* 권한을 부여해야 합니다. (3) AMI 또는 스냅샷의 시작/공유 권한을 수정하여 해당 계정을 포함시켜야 합니다. (4) 대상 계정의 IAM 보안 주체도 해당 KMS 작업 권한을 가지고 있는지 확인해야 합니다. 2단계를 건너뛰어도 공유 작업은 성공한 것처럼 보이지만, 수신 계정은 리소스를 복호화할 수 없습니다. AMI 공유만으로 충분하다고 가정하는 것이 전형적인 함정입니다.
S3 서버 측 암호화 모드
| 모드 | 키 소유자 | 교체 | CloudTrail 감사 | 비용 |
|---|---|---|---|---|
| SSE-S3 (AES-256) | AWS 관리, 숨겨짐 | 자동, 불투명 | 보이지 않음 | 키 비용 없음 |
aws/s3를 사용한 SSE-KMS | AWS | 매년 자동 | 예 | 키 비용 없음, API 요금 부과 |
| 고객 CMK를 사용한 SSE-KMS | 고객 | 선택 사항, 활성화 필요 | 예 | 키당 월 $1 + API 요금 |
| DSSE-KMS | 고객 | CMK와 동일 | 예 | 더 높음; 규제 대상 워크로드를 위한 이중 계층 |
| SSE-C | 요청별로 고객 | 수동 | 제한적 | 키 비용 없음 |
| CSE-KMS / CSE-C | 고객, 업로드 전 암호화 | 수동 | KMS 호출만 | 다양함 |
요구사항이 자동 연간 교체, CloudTrail 감사 가능성, 그리고 키 비용 최소화를 명시할 경우, 정답은 AWS 관리형 aws/s3 키를 사용하는 SSE-KMS입니다. 이 키는 비용 없이 매년 교체되며 모든 GenerateDataKey/Decrypt 호출이 기록됩니다. SSE-S3는 더 저렴하지만 키 사용 추적 기록을 남기지 않습니다. 고객 관리형 CMK는 월 1달러의 비용이 추가되며, 교체를 활성화해야만 키가 교체됩니다.
SSE-S3와 SSE-KMS를 혼동하는 것은 전형적인 PHI(개인 건강 정보) 관련 함정입니다. SSE-S3는 데이터를 암호화하지만 키 정책을 제공하지 않고, CloudTrail 가시성도 없으며, 규정 준수팀이 키를 관리할 방법도 없습니다. 따라서 키를 “관리”, “제어”, “감사” 또는 “액세스 권한 취소"해야 한다는 요구사항을 충족시키지 못합니다. 반대로, 요구사항이 단지 “최소한의 관리로 저장 시 암호화"일 뿐인데 SSE-KMS를 선택하는 것은 과도한 설계(over-engineering)입니다.
SSE-KMS를 활성화하는 것만으로는 그 자체로 무단 읽기를 방지할 수 없습니다. 만약 버킷 정책이 s3:GetObject를 허용하고 키 정책이 동일한 보안 주체에게 kms:Decrypt 권한을 부여하면, 객체는 읽을 수 있습니다. 저장 시 암호화는 물리적 미디어 유출을 방어하고 키 정책을 통해 두 번째 인증 확인을 추가하는 역할을 합니다. 이는 올바르게 범위가 지정된 버킷 정책, IAM 정책, VPC 엔드포인트 정책, 그리고 aws:PrincipalOrgID 조건을 대체하지 않습니다.
S3에서 암호화 및 TLS 강제 적용
버킷을 ‘기본적으로 암호화’하도록 설정하는 것만으로는 충분하지 않습니다. 클라이언트는 암호화 헤더를 생략하거나 재정의할 수 있습니다. 두 가지 가드레일을 계층적으로 적용해야 합니다: 기본 버킷 암호화(클라이언트가 헤더를 생략할 경우 헤더를 채워줌)와 필수 헤더 없는 PutObject를 거부하는 거부 기반 버킷 정책, 그리고 비-TLS 접근을 거부하는 구문입니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnEncryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::phi-bucket/*",
"Condition": {
"StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
}
},
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": ["arn:aws:s3:::phi-bucket", "arn:aws:s3:::phi-bucket/*"],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}
]
}
aws:SecureTransport 조건은 HTTPS를 강제하여 ‘전송 중 암호화’를 충족시킵니다. 규정 준수팀이 소유한 CMK를 사용하는 SSE-KMS와 결합하면, 이것이 바로 표준적인 PHI 저장 패턴입니다.
전송 중 암호화 vs. 저장 데이터 암호화
저장 데이터 암호화(KMS로 암호화된 EBS, RDS 스토리지, S3 객체)와 전송 중 데이터 암호화(네트워크상의 TLS)는 디스크 도난과 네트워크 가로채기라는 서로 다른 위협에 대응하는 독립적인 통제 수단입니다. RDS 인스턴스에서 KMS를 활성화하면 기본 스토리지가 보호되지만, 클라이언트-데이터베이스 세션에는 아무런 영향을 미치지 않으며, 이 세션은 기본적으로 암호화되지 않을 수 있습니다. RDS MySQL의 경우, 전송 중 데이터를 보호하려면 RDS CA 번들을 다운로드하고, 파라미터 그룹에서 require_secure_transport=ON을 설정하고, 클라이언트에서 --ssl-ca=rds-combined-ca-bundle.pem 옵션으로 연결해야 합니다. ‘저장 데이터 암호화가 켜져 있으니 충분하다’고 여기는 것은 자주 발견되는 감사 실패 사례입니다.
Secrets Manager와 Parameter Store
구성 파일, 환경 변수 또는 CloudFormation 파라미터에 있는 정적 데이터베이스 암호는 자격 증명 유출의 주요 경로입니다. AWS Secrets Manager는 KMS로 암호화된 보안 암호를 저장하고, IAM으로 제어되는 GetSecretValue를 통해 노출하며, 결정적으로 Lambda 교체 함수를 통해 자동으로 교체합니다. RDS, Aurora, Redshift, DocumentDB의 경우, AWS는 데이터베이스에 연결하여 새 암호를 생성하고, 보안 암호와 DB 사용자를 원자적으로 업데이트하며, 단일 사용자 또는 다중 사용자 전략을 지원하는 관리형 교체 Lambda를 제공합니다. 다른 시스템의 경우, createSecret, setSecret, testSecret, finishSecret의 4단계 라이프사이클을 구현하는 Lambda를 직접 작성해야 합니다.
import boto3, json
secret = json.loads(
boto3.client('secretsmanager')
.get_secret_value(SecretId='prod/aurora/app')['SecretString'])
conn = pymysql.connect(host=secret['host'],
user=secret['username'],
password=secret['password'])
애플리케이션은 (AWS SDK 캐싱 라이브러리를 사용하여) 값을 잠시 캐시하고 인증 실패 시 재연결합니다. 교체 과정은 보이지 않으며, 암호 변경을 위해 배포가 필요하지 않습니다.
SSM Parameter Store SecureString은 자동 교체가 필요 없고 비용이 주된 고려 사항일 때의 대안입니다:
| 기능 | Secrets Manager | SSM Parameter Store |
|---|---|---|
| 자동 교체 | 예; RDS/Aurora/Redshift/DocumentDB에 대해 네이티브 지원 | 네이티브 교체 기능 없음 (Advanced 티어에서 EventBridge 트리거 가능) |
| 비용 | $0.40/보안 암호/월 + API | Standard는 무료; Advanced는 유료 |
| 크기 제한 | 64KB | 4KB Standard, 8KB Advanced |
| 계정 간 공유 | 리소스 정책 | 네이티브 공유 불가 |
| 리전 간 복제 | 예 | 아니요 |
| 암호화 | KMS 필수 | SecureString에만 KMS 사용 |
SecureString 파라미터를 가져오는 주체는 키에 대한 ssm:GetParameter 와 kms:Decrypt 권한이 모두 필요합니다. KMS 권한을 누락하는 것은 가장 흔한 구성 오류 중 하나입니다. IAM 정책은 올바르게 보이지만 API 호출은 복호화 단계에서 실패합니다.
EC2, ECS, EKS, Lambda에서는 코드가 IAM 역할을 수임(assume)하고 GetSecretValue 또는 GetParameter를 호출해야 합니다. 디스크에 장기 자격 증명을 저장해서는 안 됩니다. IAM 사용자 액세스 키를 애플리케이션 코드에 직접 포함하는 것은(암호화되었더라도) 최소 권한 원칙에 위배되며 교체를 복잡하게 만듭니다.
감사 및 포렌식 도구
CloudTrail은 모든 AWS API 호출(누가, 무엇을, 언제, 어디서)을 기록합니다. 관리 계정에서 활성화된 **조직 추적(organization trail)**은 모든 계정의 이벤트를 단일 S3 버킷으로 수집하며, 이상적으로는 S3 Object Lock과 MFA Delete가 설정된 잠긴 보안 계정에 저장해야 합니다. 이를 통해 어떤 보안 주체가 볼륨을 삭제했는지, 어떤 역할이 보안 그룹을 수정했는지, 어떤 액세스 키가 UTC 03:17에 ec2:RunInstances를 호출했는지 등에 대한 불변의 포렌식 타임라인을 제공합니다. CloudWatch Logs 및 경보(루트 로그인, IAM 정책 변경)와 특정 시점의 리소스 상태 및 규정 준수 팩을 위한 AWS Config로 보완하십시오. cloudtrail:StopLogging 및 cloudtrail:DeleteTrail을 거부하는 SCP를 사용하여 무단 변경을 방지하십시오.
S3 버킷의 MFA Delete는 루트 사용자가 객체 버전을 영구적으로 삭제하거나 버전 관리를 비활성화할 때 MFA 토큰을 제시하도록 강제합니다. 이 기능은 루트 사용자만 CLI를 통해 활성화할 수 있으며, 랜섬웨어나 내부자의 삭제 행위에 대해 강력한 보호를 제공합니다.
Amazon Macie는 관리형 ML을 사용하여 S3에서 개인 식별 정보(PII), 보호 대상 건강 정보(PHI), 자격 증명, 금융 데이터를 탐지하고 심각도 등급이 매겨진 결과를 생성합니다. 이것은 탐지 도구이지 암호화 도구가 아닙니다.
위협 탐지: GuardDuty, Security Hub, Detective
Amazon GuardDuty는 VPC Flow Logs, DNS 로그, CloudTrail 관리 및 데이터 이벤트를 지속적으로 분석하며, EKS 감사 로그, S3 데이터 이벤트, EBS 악성코드 스캔, Lambda 네트워크 활동, RDS 로그인 이벤트를 위한 전용 보호 플랜을 제공합니다. GuardDuty RDS Protection은 Aurora 및 RDS에 대한 비정상적이거나 무차별 대입 인증 시도를 찾아냅니다. 이는 L4 수준에서는 연결이 합법적이기 때문에 보안 그룹이 탐지할 수 없는 동작입니다. 탐지 결과는 EventBridge, Security Hub, Detective 대시보드로 전달됩니다.
보안 그룹은 L3–L4에서만 작동합니다. 443 포트에 대해 0.0.0.0/0을 허용하는 보안 그룹은 SQL 인젝션 페이로드를 전달할 때 제 역할을 다하고 있는 것입니다. 바로 이런 공격을 막기 위해 WAF가 존재합니다. 심층 방어(Defense in depth)란 보안 그룹, WAF, Shield, GuardDuty를 함께 사용하는 것을 의미하며, 각각은 다른 서비스가 볼 수 없는 계층을 담당합니다.
DDoS: Shield Standard와 Advanced
AWS Shield Standard는 자동이며 무료로 제공되며, 모든 계정을 일반적인 L3/L4 공격(SYN 플러드, 리플렉션 공격)으로부터 보호합니다. 가시성이나 사용자 지정 완화 조치, 인력 개입 경로 없이 자동으로 실행됩니다.
AWS Shield Advanced(조직당 월 3,000달러에 데이터 전송 요금 추가)는 시나리오에서 사전 예방적 대응, 전담 대응, DDoS로 인한 스케일링에 대한 비용 보호, 또는 실시간에 가까운 공격 가시성이 언급될 때마다 필요합니다. CloudFront, Global Accelerator, ALB, CLB, Route 53, Elastic IP를 보호하며, 활성 공격 중에 사용자를 대신하여 WAF 규칙을 작성할 수 있도록 IAM 역할을 통해 사전 승인된 **Shield Response Team(SRT)**에 연중무휴 24시간 액세스를 제공합니다. ALB와 Route 53 뒤의 설계에서 관리형 탐지 및 인력 대응이 필요할 때, Shield Standard만으로는 불충분하며, 이는 반복적으로 나오는 함정입니다. Advanced는 또한 공격으로 인해 트리거된 스케일링에 대한 비용 보호를 제공합니다. “최소한의 구현 노력"이 언급되고 아키텍처에 이미 Global Accelerator나 ALB가 포함된 경우, 정답은 일반적으로 사용자 지정 Lambda@Edge를 구축하거나 CDN을 마이그레이션하는 것이 아니라 Shield Advanced를 활성화하고 AWS 관리형 WAF 규칙 그룹을 연결하는 것입니다.
애플리케이션 계층 보호: AWS WAF
AWS WAF는 CloudFront, Application Load Balancer, API Gateway, AppSync, App Runner, Cognito 사용자 풀에 연결됩니다. Network Load Balancer를 직접 보호하지는 않습니다(앞단에 CloudFront를 배치해야 함). L7 트래픽을 검사하고 SQL 인젝션, XSS, 크기 제약, 지역 차단, IP 평판, 속도 제한에 대한 규칙을 적용합니다. AWS Managed Rules는 정규식을 직접 작성할 필요 없이 AWSManagedRulesCommonRuleSet 및 AWSManagedRulesSQLiRuleSet과 같이 선별된 규칙 그룹을 제공합니다.
속도 기반 규칙은 HTTP 플러드 및 크리덴셜 스터핑에 대한 주요 방어 수단입니다. 5분 간격으로 소스 IP(또는 전달된 헤더)별 요청 수를 계산하여 공격자를 자동으로 차단합니다.
Rules:
- Name: RateLimitPerIP
Priority: 1
Statement:
RateBasedStatement:
Limit: 2000 # per 5-min window per IP
AggregateKeyType: IP
Action: { Block: {} }
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: RateLimitPerIP
SampledRequestsEnabled: true
WAF는 DDoS 서비스가 아니며, 이는 Shield의 역할입니다. WAF는 리소스 정책(TLS가 아닌 요청을 거부하는 S3 버킷 정책, 접근 가능한 버킷을 제한하는 VPC 엔드포인트 정책)과 네트워크 제어(보안 그룹, NACL)를 보완합니다. 어느 한 계층에서의 잘못된 구성으로 인해 데이터가 노출되어서는 안 됩니다.
네트워크 격리: 보안 그룹, NACL, Network Firewall
VPC 내에서 방어는 계층화됩니다.
- 보안 그룹은 상태 저장(stateful) 방식이며, ENI에 적용되고, 허용(allow) 규칙만 사용하며, 모든 규칙을 함께 평가합니다. 반환 트래픽은 자동으로 허용되므로 임시 포트를 명시적으로 열 필요가 없습니다.
- 네트워크 ACL은 상태 비저장(stateless) 방식이며, 서브넷에 적용되고, 허용(allow) 및 거부(deny) 규칙을 모두 지원하며, 규칙 번호 순서대로 평가합니다. 상태 비저장이므로 인바운드 443을 허용하는 경우, 응답을 위해 아웃바운드 임시 포트 1024–65535도 허용해야 합니다. 이를 잊으면 모든 응답이 미묘한 방식으로 유실됩니다. 보안 그룹에는 이 문제가 없습니다.
- AWS Network Firewall은 심층 패킷 검사, Suricata 호환 IPS 규칙, 도메인 기반 이그레스 필터링을 제공하며, 서브넷과 IGW/TGW 사이에 위치하여 중앙 집중식 검사를 수행합니다.
보안 그룹만으로 충분하다고 가정하는 것은 서브넷 수준의 위협과 영향 반경 제어를 무시하는 것이고, NACL만으로 충분하다고 가정하는 것은 상태 비저장 특성과 세밀하지 못한 제어 방식을 무시하는 것입니다.
함정 목록
신뢰 정책 누락. 역할의 권한 정책은 능력을 부여하고, 신뢰 정책만이 역할을 수임(assume)할 수 있는 권한을 부여합니다. 계정 간 또는 서비스 간 액세스가 작동하려면 둘 다 열려 있어야 합니다. 그렇지 않으면 호출자는 자격 증명 정책이 아무리 허용적이더라도 sts:AssumeRole에 대해 AccessDenied 오류를 받게 됩니다.
키 정책과 일치하지 않는 IAM 정책. IAM의 kms:Decrypt는 필요하지만 충분하지는 않습니다. 키 정책은 보안 주체를 직접 명시하거나 Principal: {"AWS": "arn:aws:iam::ACCOUNT:root"}를 사용하여 IAM에 위임해야 합니다. AMI 공유만 하고 키 정책을 업데이트하지 않으면 암호화된 AMI/스냅샷 공유가 실패합니다.
SCP를 권한 부여로 착각. SCP는 권한을 제한할 뿐, 부여하지 않습니다. Allow s3:* SCP는 일치하는 자격 증명 정책이 없으면 아무런 효과가 없습니다. 반대로, 허용적인 자격 증명 정책이라도 계정 경로에 있는 모든 SCP Deny에 의해 제한됩니다.
KMS가 자동으로 교체된다고 가정. 고객 관리형 CMK는 활성화하기 전까지 교체되지 않으며, 가져온 키 물질은 절대 자동으로 교체되지 않습니다.
규정 준수가 필요한 데이터에 SSE-S3 선택. SSE-S3는 키 정책, CloudTrail 가시성, 폐기 경로가 없으므로, 키를 ‘관리’, ‘제어’, ‘감사’, ‘폐기’해야 한다는 요구 사항을 충족하지 못합니다.
저장 데이터 암호화만으로 충분하다고 간주. 저장 시 암호화와 전송 중 암호화는 별개입니다. KMS를 사용하는 RDS라도 require_secure_transport=ON 설정과 클라이언트 CA 검증이 필요합니다.
버킷 정책에서 계정을 개별적으로 명시. 확장성이 없고 조직 변경 시 문제가 발생합니다. aws:PrincipalOrgID를 사용해야 합니다.
어디에든 액세스 키를 하드코딩. 사용자 데이터, .env 파일, CloudFormation 파라미터, Git 등 어디든 잘못된 방식입니다. 인스턴스 프로파일, 태스크 역할, 실행 역할, IRSA/Pod Identity 또는 Roles Anywhere를 사용해야 합니다.
일상 작업이나 정책 연결에 루트 사용자 사용. 루트 사용자는 IAM이나 SCP로 제한할 수 없으므로, 루트에 정책을 추가하는 것은 의미가 없습니다. 그렇게 하는 모든 답변은 그 자체로 틀린 것입니다.
계정 간 액세스에 IAM 사용자 사용. IAM 사용자는 다른 계정에서 수임(assume)할 수 없습니다. 대상 계정에 역할을 생성하고 소스 계정의 보안 주체가 이를 수임하도록 해야 합니다.
WAF 뒤에 NLB 배치. WAF는 NLB에 연결되지 않습니다. L7 필터링이 필요하면 NLB 앞에 CloudFront를 배치해야 합니다.
NACL에 임시 아웃바운드 규칙 누락. 상태 비저장(stateless) NACL은 명시적인 반환 경로 규칙이 필요합니다. 아웃바운드 1024–65535 규칙이 없으면 모든 인바운드 443 응답이 조용히 실패합니다.
관리형 대응에 Shield Standard 사용. Standard는 SRT 액세스가 없는 수동적 방식입니다. ‘사전 예방적 관리형 대응’이나 ‘비용 보호’는 Advanced만 충족할 수 있습니다.
이 문제 연습하기 → · ExamRoll.io에서 시간 제한 연습 →
Pass the whole exam — not just this question
You found this answer. Get every verified question and explanation in one place, and save hours of prep. Free to start.
시험 합격하기 →