Access Keys Leak Because They Exist
한국어 원문으로 표시합니다.
한 줄 요약
장기 액세스 키는 만들지 않는 것이 최선입니다. 역할을 맡아 받은 임시 자격증명은 몇 시간 뒤 스스로 만료되므로, 유출돼도 피해가 시간으로 잘립니다.
왜 이게 필요했나
깃허브 공개 저장소에 AWS 키가 커밋되는 사고는 지금도 매일 일어납니다. 봇이 몇 분 안에 찾아내 암호화폐 채굴 인스턴스를 띄웁니다. 청구서는 수천만 원 단위로 옵니다.
여기서 진짜 문제는 커밋 실수가 아니라 그 키가 존재했다는 것입니다. 장기 키는 다음 성질을 가집니다.
- 스스로 만료되지 않는다 — 사람이 회수하기 전까지 영원히 유효
- 어디서 쓰이는지 추적하기 어렵다 — 그래서 폐기가 무섭다
- 복사가 쉽다 — 문서, 채팅, 환경 변수, CI 설정에 퍼진다
어떻게 동작하나
역할을 맡는다는 것
역할은 권한 묶음이고, 신뢰 정책(trust policy) 이 "누가 이 역할을 맡을 수 있는가" 를 정합니다.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
이 역할은 EC2 인스턴스가 맡을 수 있습니다. 인스턴스에 붙여 두면 그 안에서 도는 프로그램이 키 없이 자격증명을 얻습니다. 메타데이터 서비스에서 자동으로 받아 오고, 만료 전에 자동으로 갱신됩니다.
정리하면 이렇습니다.
| 장기 액세스 키 | 역할(임시 자격증명) | |
|---|---|---|
| 만료 | 없음 | 보통 1~12시간 |
| 배포 | 파일·환경변수로 복사 | 자동 주입 |
| 회수 | 수동으로 찾아 폐기 | 만료를 기다리면 됨 |
| 추적 | 누가 쓰는지 불명확 | 역할 단위로 감사 로그 |
서비스 계정 키도 같은 문제다
쿠버네티스에서도 똑같습니다. 파드에 클라우드 키를 시크릿으로 넣는 방식은 같은 위험을 그대로 가집니다. 대신 OIDC 연합을 씁니다 — 클러스터가 발급한 서비스 계정 토큰으로 클라우드 역할을 맡습니다(AWS 의 IRSA, GCP 의 Workload Identity, Azure 의 Workload Identity). 키가 아예 존재하지 않게 되는 것이 핵심입니다.
CI 도 마찬가지입니다. 깃허브 액션이나 Gitea 러너에 장기 키를 넣는 대신 OIDC 로 역할을 맡게 하면, 저장소 시크릿에서 클라우드 키가 사라집니다.
그래도 장기 키가 필요할 때
완전히 없앨 수 없는 경우가 있습니다(외부 SaaS 연동 등). 그때는 최소한 이렇게 합니다.
- 전용 주체를 만든다 — 사람 계정의 키를 공유하지 않는다.
- 권한을 그 용도로만 좁힌다 — Condition 으로 IP·시간까지.
- 정기 교체(rotation) 를 절차로 만든다. 캘린더에 넣는다.
- 사용 흔적을 본다 — 마지막 사용 시각이 오래됐으면 지운다.
4번이 가장 값싸고 효과가 큽니다. 안 쓰는 키를 지우는 것만으로 공격면이 크게 줄어듭니다.
세션과 만료 시간
역할을 맡을 때 세션 시간을 정합니다. 짧을수록 안전하지만 너무 짧으면 긴 작업 중에 만료됩니다. 실무 기준은 이렇습니다.
- 사람의 콘솔 작업: 1시간 정도
- CI 잡: 잡의 최대 실행 시간보다 조금 길게
- 긴 배치: 자격증명 갱신을 코드가 처리하게 만든다(SDK 가 대개 해 준다)
현장에서 만나는 모습
- 저장소에서 키가 발견됨 → 즉시 폐기. 그런데 어디서 쓰는지 몰라 폐기를 미룸.
- 인스턴스에 키 파일을 놓고 씀 → 역할을 붙이면 그 파일이 필요 없다.
- 키 하나를 팀 전체가 공유 → 감사 로그에서 누가 했는지 알 수 없다.
역할을 맡을 때 조심할 것
역할이 장기 키보다 안전한 것은 맞지만, 신뢰 정책을 느슨하게 쓰면 그 안전이 통째로 사라진다. 사고가 나는 자리는 대체로 정해져 있다.
누구든 맡을 수 있게 열어 둔 역할. 신뢰 정책의 주체를 넓게 적으면 다른 계정의 누군가가 그 역할을 맡을 수 있다. 외부 협력사에 권한을 주려고 열어 둔 것이 대표적인데, 상대 계정 번호만으로는 부족하다. 그 계정 안의 누구든 맡을 수 있기 때문이다. 외부에 열 때는 양쪽만 아는 식별자를 조건으로 함께 걸어, 제3자가 그 계정을 통해 우회하지 못하게 해야 한다.
연합에서 조건을 빠뜨리는 것. CI 가 OIDC 로 역할을 맡을 때, 어느 저장소의 어느 브랜치인지를 조건으로 걸지 않으면 그 CI 서비스를 쓰는 아무 저장소나 우리 역할을 맡을 수 있다. 이것이 가장 자주 나오는 실수이고, 조건 한 줄로 막힌다.
역할을 넘겨주는 권한. 자원을 만들 때 그 자원에 역할을 붙이는 동작은, 사실상 "내가 못 하는 일을 대신 하게 만들 수 있는" 권한이다. 이 동작을 줄 때는 어떤 역할을 붙일 수 있는지까지 좁혀야 하고, 그러지 않으면 낮은 권한의 사용자가 관리자 역할을 붙인 자원을 만들어 권한을 올릴 수 있다.
사슬의 끝을 본다. 역할이 다른 역할을 맡고 그것이 또 다른 역할을 맡는 구조가 만들어지면, 최종적으로 무엇을 할 수 있는지 사람이 추적하기 어려워진다. 앞 모듈에서 말한 평가 도구가 여기서 특히 값지고, 사슬의 깊이를 규칙으로 제한해 두는 것도 방법이다.
마지막으로 임시 자격 증명도 유효한 동안에는 취소되지 않는다. 역할의 권한을 줄여도 이미 발급된 세션은 그대로 살아 있다. 그래서 세션 시간을 짧게 잡는 것이 단순한 위생이 아니라 사고가 났을 때의 대응 속도 그 자체입니다.
다음에 볼 것
권한을 실제로 좁혀 가는 순서. 처음부터 최소권한을 맞히는 건 불가능합니다.