.env에 API 키를 넣고 개발하다가 뒤늦게 .gitignore를 추가했습니다. 이제 안전할 것 같은데, GitHub에는 파일이 그대로 보입니다. 파일을 지우고 다시 커밋했더니 이번에는 예전 커밋에서 키가 나옵니다.
이 상황에서는 세 가지를 나눠 봐야 합니다. 지금 파일을 추적하고 있는지, 과거 커밋에 값이 들어 있는지, 실제로 사용할 수 있는 키가 노출됐는지입니다. 각각 필요한 조치가 다릅니다.
짧게 재현해 보기
아래 실험은 실제 서비스에 연결되지 않는 가짜 문자열만 사용했습니다. 원격 저장소가 없는 새 임시 저장소에서 Git 2.52.0으로 실행했습니다.
Bash 환경에서 다음 순서로 재현할 수 있습니다. 기존 프로젝트 대신 별도 임시 폴더를 사용하세요.
DEMO_DIR=$(mktemp -d)
cd "$DEMO_DIR"
git init -q
git config user.name "Documentation Demo"
git config user.email "demo@example.invalid"
printf 'DEMO_TOKEN=FAKE_NOT_A_REAL_SECRET_V1\n' > .env
git add .env
git -c commit.gpgsign=false commit -qm "Add fake demo configuration"
printf '.env\n' > .gitignore
git add .gitignore
git -c commit.gpgsign=false commit -qm "Ignore env files"
printf 'DEMO_TOKEN=FAKE_NOT_A_REAL_SECRET_V2\n' > .env
git status --short
git ls-files -- .env
각 명령의 출력은 다음과 같습니다. 아래 출력 블록의 # 줄과 (출력 없음) 표시는 설명용입니다.
# git status --short
M .env
# git ls-files -- .env
.env
첫 줄은 .env의 수정이 감지됐다는 뜻입니다. 두 번째 줄은 .env가 여전히 인덱스에 등록돼 있다는 뜻입니다. .gitignore에 적었다고 기존 추적이 끝나지는 않았습니다.
이어서 추적을 중단합니다.
git rm --cached -- .env
git -c commit.gpgsign=false commit -qm "Stop tracking demo env"
test -f .env && echo yes
git ls-files -- .env
git status --short --ignored
git show HEAD~2:.env
확인 명령들의 출력은 다음과 같습니다. git ls-files에서는 아무것도 출력되지 않았습니다.
# test -f .env && echo yes
yes
# git ls-files -- .env
(출력 없음)
# git status --short --ignored
!! .env
# git show HEAD~2:.env
DEMO_TOKEN=FAKE_NOT_A_REAL_SECRET_V1
로컬 파일은 남아 있고, 현재 추적 목록에서는 빠졌습니다. !! .env는 무시되는 파일이라는 표시입니다. 그런데 마지막 명령은 첫 번째 커밋에 저장된 가짜 키를 그대로 보여줍니다. 이 실험에는 커밋이 세 개 있습니다. 현재 HEAD는 추적 중단 커밋이고, HEAD~2는 첫 번째 커밋을 가리킵니다. 중간에 다른 커밋을 추가했다면 이 상대 참조가 가리키는 대상도 달라집니다.
여기까지가 이 실험에서 직접 확인한 범위입니다. 실제 유출, 원격 서버의 캐시 삭제, 기록 재작성은 실험하지 않았습니다.
왜 이런 일이 생길까?
1. .gitignore는 추적하지 않는 파일에 적용된다
Git 공식 문서는 .gitignore가 이미 추적 중인 파일에는 영향을 주지 않는다고 설명합니다. 뒤늦게 규칙을 추가해도 인덱스에 등록된 파일이 자동으로 빠지지 않습니다. Git: gitignore
현재 상태와 규칙은 따로 확인하면 됩니다. 아래 예시는 앞선 실험에서 .gitignore를 추가했지만, 아직 git rm --cached를 실행하기 전인 추적 중 시점의 결과입니다.
# 현재 인덱스에 등록돼 있는가?
git ls-files -- .env
# 추적 여부와 별개로 어떤 ignore 규칙이 맞는가?
git check-ignore -v --no-index -- .env
# git ls-files -- .env
.env
# git check-ignore -v --no-index -- .env
.gitignore:1:.env .env
# git check-ignore -v -- .env (기본 동작 비교)
(출력 없음)
--no-index를 붙인 출력에서 규칙과 대상 경로 사이에는 탭이 있습니다. 규칙은 맞았지만 이미 추적 중이었던 것입니다.
git check-ignore는 기본적으로 추적 파일을 표시하지 않으므로, 이 진단에서는 --no-index를 붙였습니다. 이 옵션이 추적을 해제해 주는 것은 아닙니다. Git: git-check-ignore
2. --cached는 인덱스에서만 제거한다
git rm --cached -- .env는 현재 작업 트리(작업 폴더)의 파일을 남겨 두고 인덱스에서 해당 경로를 제거합니다. 이미 커밋된 파일이라면 이 상태를 커밋해 새 스냅샷에 반영해야 합니다. Git: git-rm
이름 때문에 캐시에 남은 비밀까지 지우는 명령으로 오해하기 쉽지만, 실험에서 봤듯 이전 커밋의 내용은 그대로 남습니다.
또한 파일이 남는다는 설명은 이 명령을 실행한 작업 폴더에 관한 것입니다. 팀원이 삭제 커밋을 받아 적용할 때, 로컬 수정 없이 추적 중이던 파일은 삭제될 수 있습니다. 로컬 변경이 있으면 적용이 거부되거나 병합 충돌이 발생하는 등 결과가 달라질 수 있습니다. 필요한 로컬 설정은 미리 안전하게 보존하고 설정 방법을 공유해야 합니다.
3. 파일의 기록과 키의 효력은 별개다
커밋에서 문자열을 지워도 서비스가 그 키를 계속 받아 준다면 키는 여전히 사용할 수 있습니다. 반대로 발급처에서 기존 키를 폐기하면, 누군가 과거 문자열을 갖고 있어도 그 키로 접근하지 못하게 할 수 있습니다.
그래서 GitHub는 실제 비밀이 노출됐을 때 키 폐기 또는 교체를 첫 단계로 안내합니다. 기록 재작성의 필요성은 그다음에 판단합니다. GitHub: 저장소에서 민감한 데이터 제거
상황에 따라 이렇게 대응하기
아직 추적하지 않은 파일이라면
파일을 추가하기 전에 .gitignore에 경로를 적고 git check-ignore -v -- .env로 규칙을 확인합니다. 예제 설정을 공유해야 한다면 실제 값이 없는 .env.example 같은 파일을 따로 준비합니다.
커밋 직전에는 추가할 파일을 명시하고 git diff --cached로 내용을 확인하는 습관이 도움이 됩니다. 실제 키가 들어 있을 수 있는 출력은 채팅이나 이슈에 그대로 붙여 넣지 마세요.
이미 추적 중이라면
먼저 작업 상태를 확인하고 .gitignore에 규칙을 추가합니다. 그다음 필요한 파일만 지정해 추적을 중단합니다.
git status --short
# .gitignore에 .env 규칙을 추가한 뒤 실행
git rm --cached -- .env
git add .gitignore
git diff --cached --stat
# 커밋할 변경 전체를 확인한 뒤 커밋
git rm이 변경 충돌 때문에 거부되면 곧바로 -f를 붙이지 말고, 작업 폴더·인덱스·기존 커밋의 차이를 먼저 확인합니다. 저장소 전체의 추적을 한꺼번에 해제하지 않고 대상 파일만 지정하면 변경 범위를 이해하기 쉽습니다.
아직 커밋하지 않고 처음 git add만 한 파일도 인덱스에 들어 있는 상태입니다. 반대로 이미 과거 커밋에 들어갔다면, 현재 추적을 중단하는 것만으로 그 기록까지 없어지지는 않습니다.
실제 키가 외부에 노출됐다면
발급처에서 기존 키를 폐기하거나 교체하고, 이전 키가 더 이상 유효하지 않은지 확인하는 일이 우선입니다. 새 키를 사용하는 배포 환경도 함께 점검해야 합니다. 키의 사용 이력과 권한을 확인해 실제 악용 여부와 피해 범위를 조사해야 합니다. 저장소에 키가 포함됐다는 사실만으로 악용됐다고 단정할 수는 없습니다.
기록 정리가 필요하면 협업자와 범위를 합의한 뒤 git-filter-repo 등의 절차를 검토합니다. 기록 재작성은 커밋 식별자를 바꾸고 PR·서명·협업 브랜치에 영향을 줄 수 있습니다. 기존 복제본에서 옛 기록이 다시 들어오는 것도 주의해야 합니다.
강제 푸시만으로 다른 사람의 복제본이나 포크까지 지워지지는 않습니다. GitHub의 PR 참조와 캐시도 별도 조치가 필요할 수 있으며, 지원팀의 제거 지원에는 조건이 있습니다. 이 때문에 여기서는 복사해서 바로 실행하는 기록 재작성 명령을 제시하지 않습니다. 자세한 절차와 주의사항은 GitHub 공식 문서를 참고하세요.
로컬 커밋에만 들어갔고 공유하지 않았다면
공유 전 기록을 정리해 실수로 푸시하지 않도록 할 수 있습니다. 다만 원격 푸시가 없었다는 사실만으로 노출 경로가 전혀 없었다고 단정하지는 마세요. 백업·공유 폴더·로그 등 다른 경로도 확인하고, 실제 노출이 의심되면 키 교체를 우선합니다.
세 가지를 따로 확인하자
.gitignore는 추적하지 않을 파일의 규칙을 정합니다.git rm --cached는 현재 인덱스에서 파일을 제외합니다. 과거 커밋은 남습니다.- 실제로 노출된 키는 발급처에서 폐기하거나 교체해야 합니다. 기록 정리는 별도로 판단합니다.
현재 파일 목록에서 보이지 않는다는 것, 과거 기록에서 사라졌다는 것, 키를 더 이상 사용할 수 없다는 것은 서로 다른 확인 항목입니다. 이 구분을 기억하면 .gitignore를 추가한 뒤 무엇을 더 해야 하는지 판단하기 쉬워집니다.
공식 출처
- Git — gitignore: https://git-scm.com/docs/gitignore
- Git — git-rm: https://git-scm.com/docs/git-rm
- Git — git-check-ignore: https://git-scm.com/docs/git-check-ignore
- Git — git-ls-files: https://git-scm.com/docs/git-ls-files
- GitHub — 저장소에서 민감한 데이터 제거: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository
공식 문서 확인일: 2026-10-17.
검증 범위
Git 2.52.0과 2.54.0의 새 임시 저장소에서 실제 서비스에 연결되지 않는 가짜 값만 사용했습니다. 본문의 명령 순서와 출력, 추적 상태, 로컬 파일 유지, 과거 커밋 잔존을 확인했으며 원격 저장소는 연결하지 않았습니다.
실제 비밀 유출, 원격 캐시 삭제, 기록 재작성은 실험하지 않았습니다.
'Security' 카테고리의 다른 글
| QR코드는 조금 가려도 왜 읽힐까? 30% 복원의 함정 (0) | 2026.10.15 |
|---|---|
| CSV 파일을 엑셀로 열 때 생기는 수식 주입 위험과 안전하게 확인하는 방법 (0) | 2026.10.14 |
| Gemini 임시 채팅은 정말 아무 기록도 안 남을까? (0) | 2026.10.13 |
| ARTEX는 어떤 도구일까? AI 자율 침투 테스트가 주목받는 이유 (0) | 2026.10.11 |
| 내 AI 비서의 관리 화면, 외부에 열어도 될까? Hermes 대시보드 보안 점검 (0) | 2026.10.10 |
댓글