/보안/.env·API 키 git에 실수로 push 했을 때 긴급 복구 가이드
보안git시크릿관리

.env·API 키 git에 실수로 push 했을 때 긴급 복구 가이드

git에 .env·API 키를 실수로 push 했나요? git filter-repo와 BFG로 히스토리를 완전 삭제하는 명령어부터 노출 키 즉시 무효화(rotate), 재발 방지 설정까지 단계별로 정리했습니다.

.env·API 키 git에 실수로 push 했을 때 긴급 복구 가이드

.env·API 키 git에 실수로 push 했을 때 긴급 복구 가이드

방금 git push를 날렸는데 .envAWS_SECRET_ACCESS_KEY가 통째로 올라간 걸 발견했나요? 심장이 철렁할 겁니다. 하지만 지금 필요한 건 패닉이 아니라 순서대로 따라할 수 있는 응급처치입니다. 아래 순서만 지키면 됩니다.

지금 당장 할 일 우선순위

  1. (가장 먼저) 노출된 키·비밀번호를 즉시 폐기(rotate) — 삭제는 그다음
  2. git 히스토리에서 시크릿 완전 제거 후 force push
  3. 협업자에게 안내 + 재발 방지 설정

순서가 의외라고 느낄 수 있습니다. 히스토리를 지우는 것보다 키 무효화가 먼저입니다. 왜 그런지 이어서 설명하겠습니다.

함정 1 — git rm --cached와 새 커밋으로는 절대 안 지워진다

가장 흔한 실수가 이겁니다.

Bash
git rm --cached .env
git commit -m "remove .env"
git push

이렇게 하면 현재 시점의 파일 트리에서는 .env가 사라집니다. 하지만 git은 모든 변경을 커밋 객체로 영구 보관합니다. 즉, 이전 커밋에는 시크릿이 그대로 박제돼 있습니다. 직접 확인해 보세요.

Bash
# 과거 커밋의 .env 내용이 그대로 출력됩니다
git log -p -- .env

# 또는 해당 커밋을 체크아웃하면 파일이 부활합니다
git checkout <시크릿이_있던_커밋> -- .env
cat .env   # 비밀번호가 멀쩡히 보입니다

오히려 "삭제 커밋"은 여기 시크릿이 있었다고 광고하는 표지판이 되어 더 위험합니다. 결론: 새 커밋이 아니라 히스토리 자체를 다시 써야 합니다.

히스토리 완전 삭제: git filter-repo vs BFG Repo-Cleaner

두 도구 모두 히스토리에서 파일/문자열을 통째로 제거합니다. 먼저 시작 전 백업 클론을 떠두는 걸 권장합니다(git clone --mirror).

방법 A — git filter-repo (권장)

Bash
# 설치
pip install git-filter-repo      # 또는 brew install git-filter-repo

# 특정 파일을 모든 히스토리에서 제거
git filter-repo --path .env --invert-paths

# 파일은 두고 '값'만 마스킹하고 싶을 때
# patterns.txt 예: AKIA[0-9A-Z]{16}==>***REMOVED***
git filter-repo --replace-text patterns.txt

방법 B — BFG Repo-Cleaner (대용량·간편)

Bash
# 다운로드 후
java -jar bfg.jar --delete-files .env
# 또는 특정 문자열 치환 (secrets.txt에 제거할 토큰 나열)
java -jar bfg.jar --replace-text secrets.txt

# BFG는 후처리를 직접 해줘야 합니다
git reflog expire --expire=now --all
git gc --prune=now --aggressive

filter-repo는 .git/refs/original을 자동 정리하지만, 안전을 위해 reflog와 gc는 양쪽 다 돌려주는 게 좋습니다.

항목git filter-repoBFG Repo-Cleaner
속도빠름매우 빠름(대형 저장소)
유연성경로·정규식 등 매우 높음단순 파일/문자열 위주
의존성PythonJava(JVM)
추천 상황세밀한 제어가 필요할 때거대 저장소를 빠르게 청소

force push

Bash
git push --force-with-lease --all
git push --force-with-lease --tags

단순 --force 대신 --force-with-lease를 쓰세요. 다른 사람이 그 사이 푸시한 작업을 덮어쓰는 사고를 막아줍니다.

가장 중요한 단계 — 노출된 키는 "삭제"가 아니라 "폐기"

핵심 원칙: push된 순간 이미 봇이 스캔했다고 가정하라. 공개 저장소는 수 초 내에 자동 스캐너가 훑습니다. 비공개여도 안심은 금물입니다. 히스토리를 아무리 깨끗이 지워도, 이미 누군가 복사했을 수 있는 키는 무효화하지 않으면 의미가 없습니다.

주요 서비스별 폐기 위치:

  • AWS: IAM → Access keys에서 해당 키 Deactivate 후 Delete, 새 키 발급
  • OpenAI: Platform → API keys에서 Revoke 후 재발급
  • Stripe: Dashboard → Developers → API keys에서 Roll/Reveal
  • DB 비밀번호: 즉시 변경하고 애플리케이션 설정·시크릿 매니저 동기화
  • GitHub PAT/OAuth: Settings → Developer settings에서 토큰 삭제

GitHub Secret Scanning은 일부 파트너 토큰(예: GitHub Token, AWS, Stripe)을 탐지하면 발급사에 자동 통지하고, 토큰을 자동 무효화하기도 합니다. 알림이 오면 무시하지 말고 즉시 대응하세요. 2024년부터 공개 저장소에는 Push Protection이 기본 활성화되어 푸시 자체를 막기도 합니다.

실무 경험 한마디: 저는 예전에 신입 시절 비공개 레포라 괜찮겠지 하고 키 무효화를 미뤘다가, fork된 사내 미러에 그대로 남아 있던 토큰이 한 달 뒤 문제가 된 적이 있습니다. "비공개니까 천천히"는 가장 비싼 착각입니다.

협업자 영향 — 지운다고 끝이 아니다

히스토리를 다시 쓰면 다른 팀원의 로컬에는 **옛날 히스토리(=시크릿 포함)**가 그대로 남습니다. 또한:

  • 열린 PR / 캐시된 diff: GitHub PR 화면에 옛 커밋 diff가 캐시될 수 있음 → 필요 시 PR 닫고 재생성
  • fork: 포크된 저장소엔 옛 객체가 살아있음

팀에 이렇게 공지하세요.

"히스토리를 재작성했습니다. 기존 클론은 버리고 새로 clone 해주세요. 로컬에서 rebase/merge 시도 금지. 관련 키는 이미 폐기·재발급했습니다."

결론 — 재발 방지 3종 세트

같은 사고를 두 번 겪지 않으려면 오늘 바로 설정하세요.

Bash
# 1. .gitignore에 추가
echo ".env" >> .gitignore
echo "*.pem" >> .gitignore

# 2. git-secrets 설치 및 등록
brew install git-secrets
git secrets --install
git secrets --register-aws
git secrets --add 'AKIA[0-9A-Z]{16}'

# 3. pre-commit hook으로 푸시 전 차단
pip install pre-commit detect-secrets

.pre-commit-config.yaml 예시:

YAML
repos:
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.5.0
    hooks:
      - id: detect-secrets

여기에 gitleakstrufflehog를 CI에 추가하면 머지 전에 한 번 더 거를 수 있습니다. 요즘은 AI 코드 어시스턴트가 예제 키를 자동으로 채워 넣다가 그대로 커밋되는 신종 경로도 흔하니, 자동 탐지 도구를 켜두는 게 더 중요해졌습니다.

오늘 적용 체크리스트

  • 노출된 모든 키 rotate/revoke 완료
  • DB·서비스 비밀번호 변경
  • filter-repo/BFG로 히스토리 정리 후 --force-with-lease push
  • 팀에 재클론 안내, fork·PR 점검
  • .gitignore + git-secrets + pre-commit 설정

자주 묻는 질문 (FAQ)

Q. 비공개(private) 저장소인데도 키를 꼭 무효화해야 하나요? A. 네. 협업자, fork, CI 로그, 캐시 등 노출 경로가 많고 권한 설정이 언제 바뀔지 모릅니다. "삭제했으니 안전"은 위험한 가정입니다. 무조건 rotate 하세요.

Q. git filter-repo와 BFG 중 뭘 써야 하나요? A. 세밀한 경로·정규식 제어가 필요하면 filter-repo, 수 GB급 대형 저장소를 빠르게 청소하려면 BFG가 편합니다. 둘 다 작업 후 reflog expire + gc --prune=now로 마무리하세요.

Q. force push 했는데 팀원 PC엔 여전히 옛 시크릿이 남아 있어요. 어떻게 하나요? A. 히스토리 재작성은 원격만 바꿉니다. 팀원에게 기존 클론을 삭제하고 새로 clone 하도록 안내하고, 무엇보다 키 자체를 폐기했으면 남아 있어도 실질적 피해는 없습니다.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.

작성 · Content Reviewer·검토 · 사람 편집자·발행 · 2026년 6월 11일

댓글

불러오는 중...