🔥 IAM 실수 모음: 프로덕션에서 마주치는 권한 함정
강의 목차
사고 보고서를 몇 번 읽고 나면 IAM에서 제일 자주 반복되는 실수가 셋이라는 것이 보인다. 루트 사용자로 일하는 습관, 장기 Access Key의 유출과 고정화, 그리고 와일드카드의 남용. 셋 다 따로 일어나지 않는다. 서로가 서로를 증폭시킨다. 급한 배포가 있고, 권한이 부족하고, 하필 루트 계정 비밀번호가 기억난다. 그래서 루트로 들어가서 Action: "*"가 들어간 정책을 하나 더 붙이고, 급한 김에 그 권한의 Access Key를 코드에 넣는다. 한 번의 사고가 세 함정을 한꺼번에 지나간다.
이 개념들은 실제 사고에서 주로 어디에서 무너지는지 살핀다.

함정 1: 루트 사용자를 상시 계정으로 쓰는 습관
Root user가 무엇이고 왜 위험한지는 IAM이란 무엇인가: 계정 보안의 문지기에서 이미 다뤘다. 여기서는 정의 대신 사고 패턴을 본다.
내가 본 현장에서 Root 관련 사고는 세 갈래로 나타난다. 첫째는 일상 작업을 Root로 하는 조직이다. 결제 정보를 확인하거나 계정 설정을 만져야 할 때만 쓰라는 AWS의 권고가 있는데도, 콘솔 로그인이 간편하다는 이유로 Root를 평소 계정으로 쓴다. 한 번 그 습관이 굳으면 Root 계정에 MFA가 안 걸려 있는 것도 자연스럽게 따라온다. 둘째는 Root의 access key가 아직 살아 있는 레거시 계정이다. 2010년대 초반에는 AWS CLI를 쓰려고 Root access key를 만드는 일이 흔했는데, 그 키를 지금도 옛 CI 스크립트에서 발견한다. 셋째는 Root 비밀번호의 공유다. 창업 초기에 공동 창업자 세 명이 같은 비밀번호로 Root에 들어가 쓰던 환경이 인원이 늘어난 뒤에도 그대로 남는 식이다.
AWS가 이 함정에 대응한 방식은 결국 규제였다. 2024년 5월부터 AWS Organizations 관리 계정의 Root user에게 MFA를 의무화했고, 그 뒤 2025년을 지나면서 AWS는 standalone 계정과 member 계정까지 범위를 넓혔다. 지금은 모든 계정 타입에서 Root MFA가 필수이고, MFA가 없는 상태에서 콘솔에 처음 사인인한 뒤에는 35일 유예 후 AWS가 차단한다(자세한 날짜 흐름은 IAM이란 무엇인가: 계정 보안의 문지기에서 이미 짚었다). 덕분에 Root에 MFA가 없는 상태는 이제 오래 버티기가 어려워졌다.
그런데 MFA가 걸려 있다고 Root를 일상에 써도 된다는 뜻은 아니다. MFA는 도난 키를 막는 도구이지, 잘못된 사용 습관을 막지는 않는다. 가장 튼튼한 설계는 여전히 Root를 계정 생성 직후 격납고에 넣고 잊는 쪽이다. 비밀번호는 길게 만들어 패스워드 매니저에 두고, MFA는 두 개(하드웨어 키 + 백업용 패스키) 걸고, 일상 권한은 전부 IAM Identity Center: 사람 계정을 관리하는 현대적 방법에서 푼다.
함정 2: 장기 Access Key 유출과 고정
장기 Access Key가 무엇인지는 User, Group, Role: 세 가지 주체의 차이에서 다뤘다. 정의를 다시 쓰지 않고, 이 키가 사고로 가는 세 갈래 경로를 본다.
첫째 갈래는 유출이다. 장기 키의 가장 유명한 사고 경로는 공개 GitHub 저장소에 키가 커밋되는 것이다. AWS는 GitHub의 secret scanning 결과를 받아 누출된 키를 잡아내고, 그 시점에 해당 IAM User에게 AWSCompromisedKeyQuarantineV2라는 관리형 정책을 자동으로 붙인다. 이 정책은 iam:CreateAccessKey, ec2:RunInstances처럼 사고 확대에 직접 쓰이는 고위험 API를 Deny로 막는다. 커밋 시점부터 격리까지 보통 분 단위 안에 끝난다.

그런데 이 자동 격리는 만능이 아니라 부분 방어로 설계했다. 첫째, Quarantine 정책이 붙어도 모든 API가 막히는 것은 아니다. 둘째, IAM은 eventual consistency 모델이므로 정책 적용 직후 짧은 시간 동안은 이전 판단이 그대로 머문다. 셋째, GitHub가 아닌 경로로 빠져나간 키는 AWS가 자동으로 잡지 못한다. 컨테이너 이미지 레이어에 들어간 채 Docker Hub에 push되거나, 로컬 개발자의 .env 파일이 Slack에 첨부된 경우는 AWS가 다루는 범위 밖이다.
둘째 갈래는 회전 없이 수년 고정되는 키다. AWS는 장기 Access Key를 부득이 써야 할 때 90일 이내 회전을 권고한다. AWS Config의 access-keys-rotated 규칙 기본값이 90일이고, Well-Architected의 SEC02-BP05도 자격증명을 주기적으로 감사·회전하라는 권고를 얹는다. 그런데 내가 본 환경 중에는 생성된 지 4년 넘은 키가 여전히 "deploy-bot" 이름으로 활성 상태였던 곳이 있었다. 그 키로 묶인 IAM User의 비밀번호는 이미 퇴사한 사람 것이었다. 회전을 미룰수록 누가 이 키의 원래 주인이었는지, 어느 스크립트가 이 키에 의존하는지를 아는 사람이 사라진다. 문제는 회전 기술이 아니라 회전을 결정할 사람이 사라진다는 점이다.
셋째 갈래는 공유다. 여러 명이 한 키를 나눠 쓰면 사고가 터졌을 때 CloudTrail만으로 누가 무엇을 했는지 다시 짚기 어렵다. 서비스 간에도 마찬가지다. 한 키를 두 컨테이너가 나눠 쓰면 하나가 침해를 당하면 다른 하나도 함께 무너진다.
이 세 갈래를 모두 비껴가는 설계는 결국 장기 키를 만들지 않는 것이다. Assume Role: 임시 자격증명이 만들어지는 순간에서 본 STS의 임시 자격증명, Instance Profile: EC2는 IAM을 어떻게 얻는가의 메타데이터 경로, IAM Identity Center: 사람 계정을 관리하는 현대적 방법의 Permission Set이 전부 장기 키를 안 만들기 위한 장치다. "왜 그렇게까지 해야 하지?"라는 질문의 답은 이 함정 하나다.
함정 3: 와일드카드 남용
Policy JSON의 다섯 요소와 평가 순서는 Policy: JSON으로 권한을 표현하는 법과 정책 평가 흐름: Allow와 Deny가 만나면에서 다뤘다. 여기서 보는 것은 와일드카드 한 글자가 만드는 폭발 반경이다.
AWS 공식 Lambda 개발자 가이드는 대놓고 이렇게 쓴다. "Remove IAM policies that have a statement with Effect: Allow with Action: "*" over Resource: "*"." 지우라는 권고를 문서가 직접 쓴다는 건, 그만큼 흔하다는 말이기도 하다.
와일드카드가 위험한 이유는 단순히 권한이 넓어서가 아니라, 어디까지 넓은지 예측하기 어려워서다. "Action": "s3:*"는 오늘의 모든 S3 액션뿐 아니라 내일 추가되는 액션까지 자동으로 허용한다. Policy: JSON으로 권한을 표현하는 법에서 짚었던 "관리형 정책 권한 자동 확장 위험"의 같은 문제를 와일드카드가 로컬 정책에서 재현한다.
가장 위험한 단일 와일드카드 패턴은 iam:PassRole에 Resource: "*"를 거는 것이다. PassRole은 AWS 서비스에 Role을 "건네주는" 권한이고, 이 한 줄이 넓게 풀리면 그 User는 계정 안의 모든 Role을 EC2·Lambda·ECS 같은 서비스에 매달아 권한을 빌려 쓸 수 있다. 결국 그 User는 Administrator에 가까운 권한을 손에 넣는다. 공개된 AWS Privilege Escalation 연구(Rhino Security Labs 등)에서 PassRole이 여러 상승 경로의 공통 진입점으로 자주 등장하는 이유다.
이 함정을 좁히는 방법은 와일드카드 대신 두 가지 도구를 쓰는 것이다. 첫째는 naming convention. 리소스 ARN이 모두 같은 접두사를 공유하도록 코드·버킷·테이블 이름을 표준화하면 arn:aws:s3:::myorg-dev-*처럼 좁은 Resource 패턴이 가능해진다. 둘째는 Condition 키. iam:PassRole에는 iam:PassedToService와 iam:AssociatedResourceArn 두 Condition 키가 있어서, "이 Role은 ec2.amazonaws.com으로만, 특정 Launch Template 범위로만 건네줄 수 있다"처럼 좁힐 수 있다. AWS Security Blog의 PassRole 가이드가 권하는 그대로다.

세 함정이 왜 같이 일어나는가
이 셋은 서로 다른 기술적 원인처럼 보이지만, 공통 동기는 하나다. 편의. 급한 배포가 있고, 그 시점에 남은 선택지는 보통 셋 중 하나로 수렴한다. 권한이 부족하면 와일드카드로 푼다. 키가 필요하면 코드에 직접 적는다. 콘솔이 안 열리면 Root로 들어간다. 각자는 10분을 아끼지만 모이면 1년 뒤 보안 감사 시즌에 1주일을 쓴다.
이 도구들은 이 편의의 비용을 사전에 분산하는 장치들이다. IAM Access Analyzer: 과권한을 탐지하는 법의 Unused Access는 넓게 준 권한이 실제로 안 쓰이고 있음을 드러낸다. Organizations와 SCP: 조직 전체 권한 제어의 Deny list는 "Root로 특정 리전에 못 들어가게" 같은 규칙을 조직 전체 상한으로 걸어둔다. IAM Identity Center: 사람 계정을 관리하는 현대적 방법은 사람마다 장기 키를 만들 이유를 없앤다. 셋 모두 "급한 순간의 편의를 없앤다"는 같은 방향이다.
편의가 권한 사고로 바뀌는 구조
IAM은 벽돌 네 개(User·Group·Role·Policy)로 이뤄지고, 평가는 implicit deny에서 출발해 여섯 관문을 순서대로 지난다. Assume Role이 임시 자격증명의 순간을 만들고, Instance Profile이 EC2와 Role을 잇는 어댑터가 된다. Access Analyzer가 넓은 권한의 흔적을 뒤늦게나마 읽어주고, Organizations/SCP가 조직 전체 상한을 정하고, Identity Center가 사람 관리의 도구를 얹는다. 이 세 함정이 바로 그 도구들이 필요한 이유다.
VPC에서는 패킷이 서브넷, 라우팅, 보안 경계를 어떻게 지나는지 다룬다. IAM이 계정의 문지기였다면, VPC는 네트워크의 문지기다. 여기서부터는 패킷 한 개의 여정 이야기다.
참고 자료
- Secure by Design: AWS to enhance MFA requirements in 2024 (AWS Security Blog): Root user MFA 의무화의 배경과 2024년 rollout 개요
- Secure by Design: AWS enhances centralized security controls as MFA requirements expand (AWS Security Blog): standalone·member 계정 확대와 35일 유예 세부
- Security best practices in IAM (AWS 공식 문서): 장기 자격증명 회피·최소 권한·회전 권고
- Avoiding granting wildcard permissions in IAM policies (AWS Lambda Developer Guide):
Action:"*"/Resource:"*"회피 권고와 naming convention 예시 - How to use the PassRole permission with IAM roles (AWS Security Blog):
iam:PassedToService·iam:AssociatedResourceArnCondition 키 사용법 - SEC02-BP05 Audit and rotate credentials periodically (AWS Well-Architected): 자격증명 주기적 회전·감사 권고
- access-keys-rotated (AWS Config rule): 기본 90일
maxAccessKeyAge기준










