새 서비스를 급히 띄우며 DB 비밀번호를 config.js에 적고, AWS 키를 환경변수로 박아 도커 이미지에 구웠습니다. 몇 달 뒤 그 이미지가 외부 레지스트리에 잘못 공개됐고, 안에 박힌 키로 누군가 자원을 휘저었습니다. "코드에는 비밀 없음"이라 믿었지만, 비밀은 코드·이미지·깃·로그 어디에나 새어 있었습니다. 비밀을 '코드 밖'에 두는 것은 선택이 아니라 기본기입니다.
- 1비밀을 코드·깃·이미지에 두면 안 되는 이유를 설명할 수 있다
- 2시크릿 매니저가 보관·접근통제·회전·감사를 어떻게 해주는지 안다
- 3KMS의 봉투 암호화 개념과 '마스터 키는 밖으로 안 나간다'를 이해한다
- 4비밀 회전이 유출 피해를 제한하는 원리를 안다
- 5앱이 실행 시점에 권한으로 비밀을 받아오는 흐름을 그릴 수 있다
비밀은 코드 밖에 둔다
왜 하드코딩이 사고의 지름길인가
비밀번호·API 키·토큰을 코드나 깃에 박으면, 저장소 접근 = 비밀 유출입니다. 특히 깃은 이력을 보존하므로 나중에 그 줄을 지워도 과거 커밋에 그대로 남습니다 — 회수하려면 히스토리 재작성 + 키 폐기·회전이 필요합니다(Git 기본 흐름).
환경변수도 안전지대가 아닙니다. 이미지에 구워지거나, 프로세스 목록·크래시 로그·에러 트래킹에 노출될 수 있습니다. 비밀은 전용 보관소에 두고, 실행 시점에 권한으로만 받아오는 것이 원칙입니다.

위 그림처럼 하드코딩된 비밀은 코드·Git 히스토리·이미지에 남아 영구 노출됩니다. 시크릿 매니저를 사용하면 코드에는 ARN만 남고, 앱이 런타임에 안전하게 주입받습니다.

위 그림처럼 마스터 키는 KMS 내 HSM을 떠나지 않고, 데이터 키를 암호화하는 데만 씁니다. 대용량 데이터는 빠른 데이터 키로 암호화해 효율성과 보안을 동시에 얻습니다.

위 그림처럼 자동 회전은 스케줄에 따라 새 비밀을 생성·갱신·저장하고, 앱은 항상 최신 버전을 참조합니다. 유출돼도 유효 기간이 제한되므로 피해 범위를 줄일 수 있습니다.
시크릿 매니저 — 보관·통제·회전·감사
KMS와 봉투 암호화 — 마스터 키는 나가지 않는다
KMS(Key Management Service)는 암호화 키를 관리합니다. 핵심 기법이 봉투 암호화입니다:
- 데이터는 빠른 데이터 키로 암호화
- 그 데이터 키 자체를 KMS의 마스터 키로 암호화해 데이터 옆에 저장
- 복호화 때 KMS가 데이터 키를 풀어주고, 그 키로 데이터를 푼다
마스터 키는 KMS를 절대 떠나지 않아 안전하고, 대용량을 KMS로 다 보내지 않아 효율적입니다. S3·EBS·RDS의 "저장 시 암호화"가 모두 이 방식입니다(오브젝트·블록·파일 스토리지·관리형 데이터베이스(RDS)).
비밀을 평문으로 출력·로깅하지 말 것
안전한 실행 조건: 디버깅 중에도 비밀은 절대 출력하지 않는다. 존재 여부만 확인하려면 길이·마스킹(앞 2자리+****)만 출력하고, 로깅 라이브러리에 비밀 필드 마스킹 규칙을 건다.
실행 전 반드시 확인
- 비밀을 echo/print/console.log로 찍는 코드가 없는가
- 에러 메시지·스택트레이스에 비밀이 섞여 나가지 않는가
- 로그 집계 파이프라인에 민감필드 마스킹이 적용돼 있는가
- CI 로그에 환경변수 비밀이 평문으로 찍히지 않는가
echo $DB_PASSWORD # 또는 logger.info(secret)위 항목을 모두 확인한 후 복사할 수 있습니다
앱이 시작 시 자신의 역할 권한으로 비밀을 받아오는 흐름입니다. 코드엔 비밀 대신 '비밀의 이름'만 둡니다.
aws secretsmanager get-secret-value \
--secret-id prod/app/db \
--query SecretString --output text
{"username":"app","password":"S3cr3t-rotated-2026"}
aws secretsmanager get-secret-value회전이 켜져 있는지, 마지막으로 언제 바뀌었는지 확인합니다.
aws secretsmanager describe-secret --secret-id prod/app/db \
--query "{Rotation:RotationEnabled,LastChanged:LastChangedDate,LastRotated:LastRotatedDate}"
{ "Rotation": true, "LastChanged": "2026-06-01T...", "LastRotated": "2026-06-01T..." }
aws secretsmanager describe-secret- describe-secret의 RotationEnabled가 false인 운영 비밀 — 회전 미설정. 유출 시 무기한 유효하므로 자동 회전 켜기 검토
- get-secret-value를 호출하는 신원(역할)이 최소 권한인지 — 해당 비밀만 읽도록 좁혔는지, 와일드카드(*)로 모든 비밀을 열어두진 않았는지(계정과 IAM)
- 코드/이미지/CI 설정에 평문 비밀이나 액세스 키가 남아 있는지 — git grep·이미지 스캔으로 점검
- LastRotatedDate가 너무 오래됐는지 — 회전 주기 정책(예: 90일) 대비 지연되면 회전 함수 실패 가능성 점검
상황: 앱(인스턴스/함수)이 시크릿 매니저에서 비밀을 가져오지 못해 기동 실패.
원인: 앱에 붙은 IAM 역할에 해당 비밀에 대한 secretsmanager:GetSecretValue 권한이 없거나, 비밀이 KMS 키로 암호화돼 있는데 그 KMS 키 복호화 권한(kms:Decrypt) 이 빠짐. 책임 공유 모델에서 권한 설정은 고객 책임(왜 클라우드인가).
진단: aws sts get-caller-identity로 현재 역할 확인 → 그 역할 정책에 GetSecretValue + (필요시) kms:Decrypt 가 있는지 확인 → 비밀의 리소스 정책도 점검.
해결: 역할에 해당 비밀 ARN으로 범위를 좁힌 GetSecretValue 권한 부여(전체 * 금지), 커스텀 KMS 키 사용 시 kms:Decrypt도 그 키로 한정해 부여. 최소 권한으로 '필요한 비밀만' 열어줍니다.
시크릿 관리는 보안 감사의 단골 점검 항목입니다. "비밀을 어떻게 관리하나요?"에 좋은 답은 "시크릿 매니저에 두고 IAM으로 접근 제한, 자동 회전, 저장 데이터는 KMS 봉투 암호화, 코드·깃·로그엔 비밀 0" 입니다. 반대로 "환경변수에 다 넣어요"는 감점입니다.
PM 관점에서도 연동 요구사항에 "비밀은 시크릿 매니저 경유", "키 회전 주기", "로그 마스킹"을 인수 기준에 넣으면 사고를 예방합니다. 깃에 키가 새는 사고는 거의 모든 팀이 한 번은 겪으므로, 사전 차단(pre-commit 시크릿 스캐너)과 사후 대응(즉시 폐기·회전) 절차를 함께 둡니다(CI/CD 파이프라인). 이 주제는 계정과 IAM의 권한 관리와 한 쌍입니다.
다음 단계로는 이 트랙에서 배운 IAM·네트워크·스토리지·비밀 관리를 코드로 묶는 IaC와 Terraform, 그리고 전체를 관측·통제하는 관측과 거버넌스로 이어집니다.