저장소에 커밋된 비밀 키를 지우려다 filter-branch를 쓰면 안 되는 이유
문제 발생
.env 파일이 실수로 커밋되어 API 키가 저장소 히스토리에 남았습니다. 검색해서 나온 명령을 그대로 실행했습니다.
git filter-branch --index-filter 'git rm --cached --ignore-unmatch .env' HEAD40분이 지나도 끝나지 않았고, 끝난 뒤에는 태그 몇 개가 사라져 있었습니다.
원인 분석
git filter-branch는 공식 문서가 직접 사용을 권하지 않는 명령입니다. 문서 첫머리의 경고를 그대로 옮기면, 이 명령은 "의도한 히스토리 재작성을 눈에 잘 띄지 않게 망가뜨리는 함정이 무수히 많고, 성능이 형편없어서 그런 문제를 조사할 시간조차 주지 않는다"고 되어 있습니다. 그리고 이 안전성·성능 문제는 하위 호환을 지키면서는 고칠 수 없다고 못박습니다.
문서가 드는 성능 문제는 구조적입니다. 커밋마다 전체 파일을 체크아웃하므로, 파일 10만 개 × 커밋 10만 개면 실제 고유 blob이 50만 개뿐이어도 10^10번의 작업이 발생합니다. 여기에 커밋마다 프로세스를 새로 띄우고, 셸로 작성돼 있습니다.
안전성 쪽은 더 곤란합니다 — OS(BSD/GNU userland) 차이로 같은 명령이 조용히 다르게 동작하고, 공백이나 비ASCII가 들어간 파일 이름이 잘못 처리되며, 주석 태그가 경량 태그로 바뀌고, 필터가 깨져도 오류 없이 잘못된 결과를 냅니다.
공식 문서의 권고는 하나입니다 — git filter-repo 같은 다른 도구를 쓰십시오.
해결 방안
-
먼저 키를 폐기하고 재발급합니다. 이게 첫 번째입니다. 히스토리를 다시 써도 이미 clone·fork·CI 캐시로 퍼진 사본은 회수되지 않습니다. 재작성은 유출을 되돌리는 수단이 아니라 뒷정리입니다.
-
filter-repo를 씁니다. 별도 설치가 필요한 파이썬 스크립트지만, 위 문제들을 피하도록 만들어졌습니다.
git clone --mirror [email protected]:team/app.git app-mirror
cd app-mirror
git filter-repo --invert-paths --path .env-
재작성이 무엇을 뜻하는지 알고 시작합니다. 공식 문서의 경고대로, 재작성된 히스토리는 모든 객체의 이름(해시)이 달라지고 원래 브랜치와 다시 합쳐지지 않습니다. 팀 전원이 다시 clone하거나 각자 브랜치를 옮겨 붙여야 합니다.
-
커밋 하나로 해결되는 문제라면 재작성하지 않습니다. 문서도 같은 조언을 합니다 — 단순한 커밋 하나로 충분하다면 이 명령을 쓰지 마십시오. 아직 push하지 않았다면 그냥 되돌리는 편이 낫습니다.
git rm --cached .env
echo ".env" >> .gitignore
git commit --amend --no-edit # 아직 공유 전인 경우에만-
다시 안 생기게 막습니다.
.gitignore는 이미 추적 중인 파일에는 적용되지 않으므로, 위처럼--cached로 추적을 먼저 끊어야 합니다. 커밋 훅이나 CI에서 비밀값 스캐너를 돌리면 사람이 기억하지 않아도 됩니다. -
작업은 항상 미러 사본에서 합니다. 원본 저장소를 직접 재작성하지 않으면, 결과가 이상할 때 버리기만 하면 됩니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.