AWS 기초 #6 보안 기본: MFA, 키 회전, 최소 권한

10 분 소요

#2 IAM에서 권한 모델을, #5에서 SSO까지 봤습니다. 이 글은 그 위에 운영에서 통하는 보안 가드레일을 한 번에 정리하겠습니다.

AWS 보안 사고의 90%는 다음 중 하나입니다.

  1. 루트 / 사용자 비밀번호 탈취 (피싱). MFA가 없어서
  2. 액세스 키가 git / 슬랙 / 로그에 노출
  3. 너무 넓은 권한이 침해 시 피해를 키움
  4. CloudTrail / GuardDuty가 꺼져 있어 사건을 못 발견

이 네 가지에 가드레일을 세우면 사고 확률이 한 자릿수로 떨어집니다.

MFA: 가장 중요한 한 가지 #

비밀번호 한 줄로 끝나는 인증은 2026년의 운영 수준이 아닙니다. 피싱 한 번이면 비밀번호는 흘러갑니다. **MFA (Multi-Factor Authentication)**는 두 번째 요소 (보통 휴대폰 앱)의 6자리 코드를 추가로 요구합니다.

MFA 종류 #

종류무엇추천
Virtual MFA (TOTP)휴대폰 앱 (Google Authenticator, 1Password, Authy)표준. 거의 모든 경우
하드웨어 MFAYubiKey 같은 USB 키루트 / 고특권. 최강
U2F / WebAuthn브라우저 + 하드웨어 키운영 자격 격상
SMS문자 메시지사용 금지 (SIM swap 공격)

루트는 하드웨어 MFA가 이상적이고, 일반 사용자는 virtual MFA로 충분합니다.

루트 사용자 MFA 활성화 #

가입 직후 첫 작업으로 즉시.

루트 MFA
콘솔 (루트 로그인) → 우측 상단 사용자 메뉴 → Security credentials
→ Multi-factor authentication (MFA) → Assign MFA device
→ Virtual MFA / Hardware MFA 선택
→ QR 코드를 휴대폰 앱으로 스캔
→ 연속 두 코드 입력 (앱이 30초마다 새 코드)

이후 루트 로그인 때마다 비밀번호 + 6자리 코드.

IAM 사용자에 MFA 강제 #

루트만 켜는 건 부족합니다. 모든 IAM 사용자에 강제해야 합니다. 두 가지 방법.

방법 1: 정책으로. “MFA가 켜진 세션이 아니면 거의 모든 액션 거부”

IAM 사용자 MFA 강제 정책
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSelfManageCredentials",
      "Effect": "Allow",
      "Action": [
        "iam:ChangePassword",
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:GetUser",
        "iam:ListMFADevices",
        "iam:ResyncMFADevice"
      ],
      "Resource": [
        "arn:aws:iam::*:user/${aws:username}",
        "arn:aws:iam::*:mfa/${aws:username}"
      ]
    },
    {
      "Sid": "DenyAllExceptListedIfNoMFA",
      "Effect": "Deny",
      "NotAction": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:GetUser",
        "iam:ListMFADevices",
        "iam:ResyncMFADevice",
        "iam:ChangePassword",
        "sts:GetSessionToken"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" }
      }
    }
  ]
}

이 정책을 모든 사용자가 속한 그룹에 붙이면 MFA 없이는 사실상 아무것도 못 합니다. MFA 등록만 예외입니다.

방법 2: SSO (#5)

IAM Identity Center 사용자에는 콘솔 / CLI 로그인 시 MFA가 자동 강제됩니다. 정책을 안 짜도 됩니다.

첫 로그인 시 MFA 등록 강제 흐름 #

신규 IAM 사용자가 처음 로그인하면 위 정책으로 MFA 등록 외엔 아무것도 못 합니다. 자기 MFA를 등록하면 그 다음부터 정상 사용할 수 있습니다. 이 흐름이 표준입니다.

액세스 키 회전: 90일이 표준 #

#4에서 본 액세스 키. 이 키는 시간이 지나면 노출 위험이 누적됩니다.

회전 정책 #

항목권장 회전 주기
사용자 액세스 키90일
CI / CD 키60일 (또는 OIDC로 전환)
서비스 계정 키30~60일
임시 자격 (SSO / Role)회전 불필요 (자동으로 단기 발급)

회전 절차 #

키를 한 번에 두 개까지 가질 수 있는 IAM의 특성을 활용합니다.

안전한 회전 절차
# 1) 새 키 발급 (이제 활성 키 2개)
aws iam create-access-key --user-name curtis

# 2) 모든 환경에서 새 키로 교체 (CI 환경 변수, ~/.aws/credentials, etc.)

# 3) 며칠 모니터링. 옛 키 사용이 있는지
#    CloudTrail 또는 IAM credential report로 확인

# 4) 옛 키 비활성화 (삭제는 아직 X, 롤백용)
aws iam update-access-key --user-name curtis --access-key-id AKIA-OLD --status Inactive

# 5) 일주일 더 모니터링 → 정말 안 쓰면 삭제
aws iam delete-access-key --user-name curtis --access-key-id AKIA-OLD

IAM Credential Report: 회전 점검 #

CSV 한 장으로 모든 사용자의 키 / MFA / 활동 상태를 봅니다.

Credential Report 받기
aws iam generate-credential-report
aws iam get-credential-report --query Content --output text | base64 -d > report.csv
report.csv의 흥미로운 컬럼
user
mfa_active
access_key_1_active
access_key_1_last_rotated
access_key_1_last_used_date
access_key_2_active
access_key_2_last_rotated
password_last_used

90일 넘은 키, MFA 안 켠 사용자, 한 달 미사용 사용자를 한눈에 확인할 수 있습니다.

노출된 키 발견 시 #

코드에 키를 push 했다면. 봇이 발견하기까지 보통 분 단위.

즉시 해야 할 일 (시간 순) #

0분: 키 비활성화
aws iam update-access-key --user-name <user> --access-key-id <키ID> --status Inactive
5분: 새 키 발급, 옛 키는 그대로 비활성
aws iam create-access-key --user-name <user>
10분: 옛 키 사용 흔적 확인 (CloudTrail)
# 콘솔 → CloudTrail → Event history
# AccessKeyId로 검색 → 의도하지 않은 사용 있는지
30분: 옛 키 삭제
aws iam delete-access-key --user-name <user> --access-key-id <키ID>
git history 청소
# BFG Repo-Cleaner 또는 git filter-repo로 history에서 키 제거
# (이미 push 됐다면 history 청소만으론 안전 X, 키 삭제가 더 중요)

AWS가 자동으로 도와주는 도구 #

AWS는 GitHub 등 공개 저장소를 스캔해 노출 키를 발견하면:

  • 사용자에게 즉시 이메일
  • 일부 경우 자동으로 키 비활성화 + 정책 첨부

이건 보조 안전망입니다. 직접 발견이 더 빠릅니다.

IAM Access Analyzer: 너무 넓은 권한 찾기 #

내 계정의 정책 / 자원 정책 (S3 버킷 정책, KMS 키 정책 등)을 분석해 외부 접근이 가능한 부분을 찾는 도구입니다. 무료.

활성화 #

활성화
콘솔 → IAM → Access Analyzer → Create analyzer
- Type: Account / Organization
- Name: my-account-analyzer

활성화 후 24시간 안에 외부 접근 가능 자원의 목록이 나옵니다.

무엇을 잡아 주는가 #

자원위험
S3 버킷. public read누구나 객체 읽기
S3 버킷. 다른 계정에 권한의도한 설정인지 확인 필요
KMS 키. 외부 사용암호화 키 노출
IAM Role. 외부 trust다른 계정이 빌릴 수 있음
Lambda. 외부 호출 권한누구나 호출 가능
RDS 스냅샷 / SQS / SNS / Secrets Manager / EBS / ECR. public데이터 / 메시지 노출

정책 검증 #

새 정책을 만들 때 Access Analyzer가 권장 사항도 보여줍니다.

  • 사용 안 하는 권한
  • 너무 넓은 와일드카드
  • 조건 없는 경우에 추가 권장

Action Last Accessed: 미사용 권한 찾기 #

각 IAM 사용자 / 역할에 대해 마지막으로 사용한 액션을 보여 줍니다. 90일 미사용 권한은 좁힐 후보입니다.

CLI에서
aws iam generate-service-last-accessed-details --arn arn:aws:iam::123:role/MyRole
# (잠시 후)
aws iam get-service-last-accessed-details --job-id ...

최소 권한: 통하는 패턴 #

“필요한 만큼만, 필요한 곳에만.” 이상은 쉬운데, 운영에서 어떻게 실천하는지가 관건입니다.

패턴 1: 넓게 시작 → 좁히기 #

처음부터 완벽한 권한을 짜기는 어렵습니다. 다음 흐름이 현실적입니다.

  1. 시작은 PowerUserAccess / 서비스의 *FullAccess
  2. 일주일 운영 후 Access Analyzer의 Action Last Accessed 확인
  3. 안 쓰는 서비스 / 액션 제거
  4. 와일드카드를 ARN으로 좁힘
  5. 조건 (Condition) 추가

이 사이클을 분기마다 반복합니다.

패턴 2: 사용자 ↔ 역할 분리 #

#2 패턴의 재확인.

  • 사람 = SSO (#5)
  • 머신 = Role (인스턴스 프로파일, 실행 역할, OIDC)
  • CI/CD = OIDC + Role (GitHub Actions, GitLab)

영구 액세스 키가 거의 사라지는 구조입니다.

패턴 3: 권한 경계 (Permission Boundary) #

“이 사용자는 무엇을 하든 이 한도 안에서만.” 신입 개발자에게 IAM 권한을 주되, 그가 새 정책 / 사용자를 만들어 자기 권한을 늘리는 사고를 차단합니다.

권한 경계의 모양
{
  "Effect": "Allow",
  "Action": [
    "ec2:*",
    "s3:*",
    "rds:*",
    "logs:*"
  ],
  "Resource": "*",
  "Condition": {
    "StringEquals": { "aws:RequestTag/env": "dev" }
  }
}

이 경계가 붙은 사용자는 dev 환경 자원 외엔 만들 수 없습니다. 자기 정책으로도 마찬가지입니다.

패턴 4: 환경 분리 #

  • 계정 분리 (Organizations). 가장 강한 방어선
  • VPC 분리. 같은 계정이라도 prod VPC와 dev VPC를 분리 (중급 #1)
  • 태그 분리. env=prod 태그로 권한 / 비용 분리 (#3의 태그 전략)

패턴 5: Break-glass 역할 #

평소엔 ReadOnly만, 사고 시에 잠시 Admin으로 격상. SSO Permission Set을 두 개로 분리.

항목평소사고 시
사용ReadOnlyBreak-glass-Admin (1시간)
알림(없음)Slack 채널에 자동 알림
감사(없음)CloudTrail로 모든 액션 기록

CloudTrail: 누가 무엇을 했는가 #

CloudTrail은 계정 안의 모든 API 호출을 기록합니다. 가입 직후 자동으로 활성화 (지난 90일 이벤트 무료 조회). 운영용은 Trail을 만들어 S3에 영구 저장.

가입 직후 점검 #

활성화 확인
콘솔 → CloudTrail → Trails
→ Multi-region Trail 한 개 만들기 (운영 표준)
- 이름: my-trail
- Storage: 새 S3 버킷
- Log file SSE-KMS encryption: 켜기
- Log file validation: 켜기 (변조 감지)

CloudTrail의 두 종류 이벤트 #

종류무엇비용
Management eventsAPI 호출 (RunInstances, DeleteBucket 등)무료 (한 번)
Data eventsS3 객체 접근, Lambda 호출 등유료 (양 많음)

대부분의 Trail은 management만. data events는 특정 버킷 / 함수에만 켭니다.

자주 쓰는 쿼리 #

콘솔 Event history에서 검색:

  • AccessKeyId. 노출 키 사용 흔적
  • UserName. 한 사용자가 한 일
  • EventName. ConsoleLogin, DeleteBucket, RunInstances
  • 시간 범위

CloudTrail Lake 또는 Athena로 SQL 분석도 가능합니다 (큰 조직).

GuardDuty: 위협 탐지 자동화 #

GuardDuty는 CloudTrail / VPC Flow Logs / DNS 로그를 머신러닝으로 분석해 의심스러운 활동을 잡아 줍니다.

잡아 주는 예 #

패턴설명
EC2가 비정상 지역에서 통신침입 / C&C
액세스 키가 비정상 지역에서 사용키 노출 후 사용
Cryptocurrency 채굴 트래픽침입 후 채굴
EC2의 비정상 포트 스캔측면 이동
Tor exit node와의 통신의심 트래픽
IAM 사용자의 비정상 행동자격 도용

활성화 #

활성화
콘솔 → GuardDuty → Get started → Enable
- 30일 무료 평가
- 무료 평가 후 데이터 양 기반 과금 (보통 \$10~50 / 월 / 작은 운영)

운영 단계에선 반드시 켜기. GuardDuty 1년 비용보다 사고 한 건의 피해가 훨씬 큽니다.

Security Hub: 보안 상태 통합 #

여러 보안 도구의 결과를 한곳에 모음. CIS Benchmark, AWS Foundational Security Best Practices 등 표준 점검을 자동 실행.

활성화
콘솔 → Security Hub → Enable
- 권장 표준 모두 켜기
- GuardDuty / Access Analyzer / Inspector 결과가 통합됨

가입 직후엔 살짝 과합니다. 한 분기 후 자원이 일정 수준 이상이 되면 켜기.

자주 만나는 사고 사례 #

사례 1: GitHub에 액세스 키 push → 가상화폐 채굴 #

가장 흔한 시나리오. 분 단위로 봇 발견, 시간 단위로 비용 누적.

대응: 즉시 키 비활성화 → 새 키 → CloudTrail로 사용 흔적 → 가능하면 계정 자체 격리. 청구 분쟁 시 AWS 지원에 연락. 대부분 무료 티어 사고는 소급 면제 (반복은 안 됨).

예방: pre-commit hook (gitleaks, truffleHog), GitHub secret scanning, 로컬에 키 자체 안 두기 (SSO).

사례 2: 너무 넓은 S3 버킷 정책 #

“우리 회사 사람들이 다 볼 수 있게” 하려고 Principal: "*"를 추가하면 public read가 되고, 검색 엔진에 인덱싱됩니다.

대응: Access Analyzer가 잡아 줌. S3 Block Public Access가 계정 / 버킷 단위로 강제.

예방: S3 Block Public Access 항상 켜기, 정말 public이 필요한 경우는 CloudFront + Origin Access Control 패턴.

사례 3: MFA 없는 루트 비밀번호 피싱 #

“AWS 결제 알림” 가짜 메일 → 가짜 로그인 페이지 → 비밀번호 입력으로 이어집니다. 루트 MFA가 없으면 그대로 침해됩니다.

대응: 비밀번호 즉시 변경, 모든 자원 점검, AWS 지원 연락.

예방: 루트 MFA 필수 (이상적으로 하드웨어), 일상 작업은 IAM / SSO로.

사례 4: 퇴사자의 액세스 키가 살아 있음 #

퇴사 처리 후에도 IAM 사용자 / 키가 그대로 남습니다. 몇 달 뒤 사고로 이어집니다.

대응: 즉시 사용자 비활성화 / 삭제 + Trail 점검.

예방: 퇴사 체크리스트에 IAM 정리 포함. SSO면 IdP에서 비활성화하면 끝.

사례 5: CloudTrail이 꺼짐 #

침해 조사를 시작하려는데 사건 시점의 로그가 없습니다. 누군가 (의도적으로) Trail을 껐거나 처음부터 안 켠 것입니다.

대응: 이미 늦음. 가능한 다른 로그 (CloudWatch, GuardDuty findings)로 부분 복원.

예방: Trail 활성화 + Log file validation + S3 객체 잠금. SCP (Service Control Policy)로 CloudTrail 비활성화 자체를 거부.

자주 만나는 함정 #

1) MFA만 켜고 끝 #

MFA가 켜져 있어도 액세스 키가 노출되면 키는 그대로 작동합니다 (CLI 호출은 MFA와 무관합니다, #4의 자격 체인). 키도 같이 신경 쓰거나, SSO로 키 자체를 줄입니다.

2) 회전을 약속만 하고 안 함 #

Credential Report로 정기 점검을 자동화합니다. 90일 넘은 키는 Slack으로 알립니다.

3) iam:PassRole의 광범위 허용 #

iam:PassRole = *은 사실상 권한 상승이 가능합니다. 역할 ARN으로 좁힙니다.

4) 조건을 너무 강하게 → 본인이 잠김 #

aws:SourceIp로 회사 IP만 허용했는데 본인이 외출 중일 수 있습니다. 콘솔 IP 가드는 마지막에 적용하고, 우회 통로 (VPN / 비상 사용자)를 둡니다.

5) GuardDuty 끄기 #

“비용 아까워서” 끄는 경우가 있는데, 한 사건이 GuardDuty 1년 비용보다 큽니다. 운영에선 항상 켭니다.

6) Security Hub 발견을 안 봄 #

켜고 알림을 안 보면 의미가 없습니다. 주간 리뷰나 EventBridge로 Slack에 알립니다.

정리 #

이번 글에서 잡은 것:

  • MFA. 모든 사용자에 필수입니다. 루트는 가능하면 하드웨어, 일반은 virtual TOTP를 쓰고, SMS는 금지합니다.
  • MFA 강제 정책. IAM 사용자에 정책으로 강제합니다. SSO면 자동입니다.
  • 액세스 키 회전 90일. 새 키 발급 → 교체 → 옛 키 비활성 → 일주일 후 삭제 순입니다. Credential Report로 점검합니다.
  • 노출 시. 즉시 비활성화하고, CloudTrail로 사용 흔적을 확인하고, 새 키로 바꾼 뒤 삭제합니다.
  • IAM Access Analyzer. 외부 접근 가능 자원, 정책 검증, Action Last Accessed (미사용 권한)를 잡아 줍니다.
  • 최소 권한 패턴. 넓게 시작 → 좁히기, 사용자/역할 분리, 권한 경계, 환경 분리, Break-glass를 씁니다.
  • CloudTrail. multi-region Trail 한 개 + S3 + log validation으로 구성합니다. SCP로 비활성화를 차단합니다.
  • GuardDuty. 머신러닝 위협 탐지입니다. 운영 필수입니다.
  • Security Hub. 표준 점검 통합입니다. 자원이 일정 수준 이상일 때 켭니다.
  • 사고 사례. 키 push, 넓은 S3 정책, 루트 피싱, 퇴사자 키, Trail 꺼짐입니다.
  • 함정. MFA만으로 안전하다는 착각, 회전 약속만 하기, PassRole 와일드카드, 너무 강한 IP 조건, GuardDuty 끄기, Hub 결과 미관리입니다.

다음: CloudWatch #

이 시리즈의 마지막입니다. 모든 운영의 눈인 CloudWatch를 정리하겠습니다.

#7 CloudWatch 입문: 로그 / 메트릭에서는 Logs / Metrics / Alarms / Dashboards의 구성, 로그 그룹과 retention, Metric Filter, Logs Insights 쿼리 기초까지 다루겠습니다.

X