리포지토리에서 중요한 데이터 제거 정보
git-filter-repo와 같은 도구를 사용하여 리포지토리의 기록을 변경할 때는 그 의미를 이해하는 것이 중요합니다. 기록을 다시 작성하려면 공동 작업자와 신중하게 조정하여 성공적으로 실행해야 하며 관리해야 하는 여러 가지 부작용이 있습니다.
제거해야 하는 중요한 데이터가 비밀(예: 암호/토큰/자격 증명)인 경우와 마찬가지로 첫 번째 단계로 해당 비밀을 해지 및/또는 회전해야 합니다. 비밀이 취소되거나 갱신되면 더 이상 액세스에 사용할 수 없으며, 이것이 문제 해결에 충분할 수 있습니다. 기록을 다시 작성하고 비밀을 제거하는 추가 단계를 거치는 것은 보증되지 않을 수 있습니다.
역사를 다시 쓰기의 부작용
기록을 다시 작성하면 다음과 같은 부작용이 발생할 수 있습니다.
- 재확산 위험 높음: 안타깝지만, 중요한 데이터를 리포지토리에 다시 푸시하여 더 큰 혼란을 만들기 쉽습니다. 동료 개발자가 다시 쓰기 전의 복제본을 가지고 있고 다시 쓰기한 후 단순히
git pull을 실행된 후git push를 실행하면 중요한 데이터가 반환됩니다. 복제본을 삭제하고 다시 복제하거나 여러 단계를 신중하게 진행하여 먼저 복제본을 정리해야 합니다. - 다른 개발자의 작업 손실 위험: 다른 개발자가 정리를 시도하는 동안 중요한 데이터가 포함된 분기를 계속 업데이트하는 경우 정리를 다시 실행하거나 작업을 취소해야 합니다.
- 변경된 커밋 해시: 기록을 다시 작성하면 중요한 데이터 및 이후에 제공된 모든 커밋을 도입한 커밋의 해시가 변경됩니다. 변경되지 않는 커밋 해시에 의존하는 모든 도구나 자동화가 손상되거나 문제가 발생합니다.
- 분기 보호 문제: 강제 푸시를 방지하는 분기 보호가 있는 경우 중요한 데이터를 제거하려면 해당 보호를 해제(적어도 일시적으로)해야 합니다.
- 닫힌 풀 리퀘스트의 손상된 차이 보기: 중요한 데이터를 제거하기 위해서는 풀 리퀘스트에서 차이 보기를 표시하는 데 사용되는 내부 참조를 제거해야 하며, 이로 인해 해당 차이를 더 이상 볼 수 없습니다. 이는 중요한 데이터를 도입한 PR뿐만 아니라 중요한 데이터 PR이 병합된 후 기록 버전에서 빌드되는 PR에도 해당됩니다(이후 PR이 중요한 데이터를 사용하여 파일을 추가하거나 수정하지 않은 경우에도 마찬가지).
- 열린 끌어오기 요청과의 잘못된 상호 작용: 변경된 커밋 SHA는 다른 PR diff로 인해 발생하며, 이전 PR diff에 대한 주석이 무효화되고 손실되어 작성자와 검토자에게 혼동을 줄 수 있습니다. 리포지토리에서 파일을 제거하기 전에 모든 열려 있는 끌어오기 요청을 병합하거나 닫는 것이 좋습니다.
- 커밋 및 태그에 대한 서명 손실: 커밋이나 태그에 대한 서명은 커밋 해시에 따라 달라집니다. 커밋 해시는 기록 다시 쓰기에 의해 수정되므로 서명은 더 이상 유효하지 않으며 많은 기록 다시 쓰기 도구(
git-filter-repo포함)는 단순히 서명을 제거합니다. 실제로git-filter-repo는 중요한 데이터 제거 이전의 커밋에 대한 커밋 서명과 태그 서명을 제거합니다. 필요한 경우--refs옵션을git-filter-repo으로 변경하여 이 문제를 우회적으로 해결할 수 있습니다. 다만, 기록에 민감한 데이터가 포함되어 있거나 해당 민감한 데이터를 도입한 커밋이 포함된 모든 참조를 반드시 지정하도록 주의해야 합니다. - 다른 사용자를 중요한 데이터로 직접 연결: Git은 커밋 식별자에 기본 제공된 암호화 검사를 사용하여 설계되었으므로 악의적인 개인이 서버에 침입하여 기록을 수정할 수 없습니다. 이는 보안 관점에서 유용하지만 중요한 데이터 관점에서 중요한 데이터를 삭제하는 것은 매우 관련된 조정 프로세스라는 것을 의미합니다. 또한 기록을 수정할 때 기존 클론이 있는 단서가 있는 사용자는 기록 차이를 확인하고 이를 사용하여 중앙 리포지토리에서 제거한 복제에서 중요한 데이터를 빠르고 쉽게 찾을 수 있습니다.
중요한 데이터 노출 정보
리포지토리에서 중요한 데이터를 제거하려면 다음 네 가지 고급 단계가 포함됩니다.
- git-filter-repo를 사용하여 리포지토리를 로컬로 다시 작성
- 로컬로 다시 작성된 기록을 사용하여 GitHub 리포지토리 업데이트
- 동료와 협력하여 존재하는 다른 클론을 정리
- 반복 방지 및 향후 중요한 데이터 유출 방지
기록을 다시 작성하고 강제로 푸시하는 경우 중요한 데이터가 있는 커밋은 다른 곳에서도 계속 액세스할 수 있습니다.
- 리포지토리의 모든 클론 또는 포크에서
- GitHub의 캐시된 뷰에서 SHA-1 해시를 통해 직접
- 이를 참조하는 풀 리퀘스트를 통해
당신의 리포지토리 복제본에서 다른 사용자의 중요한 데이터를 제거할 수 없습니다. 다른 복제본을 정리하는 방법: 동료들의 복제본이 포함된 git-filter-repo 매뉴얼의 지침을 보내어 그들이 직접 처리하도록 해야 합니다. 그러나 GitHub에 연락하면 사이트 관리자에게 문의의 풀 리퀘스트에서 캐시된 보기와 민감한 데이터에 대한 참조를 영구적으로 제거할 수 있습니다.
중요한 데이터를 도입한 커밋이 모든 포크에 있는 경우 계속 액세스할 수 있습니다. 포크 소유자와 조정하여 중요한 데이터를 제거하거나 포크를 완전히 삭제하도록 요청해야 합니다.
리포지토리의 기록을 다시 작성하기로 결정할 때 이러한 제한 사항을 고려하세요.
git-filter-repo를 사용하여 로컬 리포지토리의 기록에서 파일 제거
-
git-filter-repo도구의 최신 릴리스를 설치합니다. 적어도 버전 2.47을 의미하는--sensitive-data-removal플래그가 있는 버전이 필요합니다.
git-filter-repo는 수동으로 설치하거나 패키지 관리자를 사용하여 설치할 수도 있습니다. 예를 들어, HomeBrew를 사용하여 도구를 설치하려면brew install명령을 사용하세요.brew install git-filter-repo자세한 내용은 __ 리포지토리의 INSTALL.md를 참조하세요.
-
로컬 컴퓨터에 리포지토리를 복제합니다. 리포지토리 복제을(를) 참조하세요.
git clone https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY -
리포지토리의 작업 디렉터리로 이동합니다.
cd YOUR-REPOSITORY -
git-filter-repo명령을 실행하여 중요한 데이터를 정리합니다.모든 분기/태그/참조에서 특정 파일을 삭제하려면
PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA를 파일 이름뿐만 아니라 제거하려는 파일의 git 경로로 바꾸는 다음 명령을 실행합니다(예:src/module/phone-numbers.txt).git-filter-repo --sensitive-data-removal --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA중요
중요한 데이터가 있는 파일이 다른 경로에 있는 경우(이동했거나 또는 이름이 변경되었기 때문에) 해당 파일에 대한 추가
--path인수를 추가하거나 이 명령을 두 번째로 실행하여 대체 경로의 이름을 지정해야 합니다.리포지토리의 기록에서 발견된 바이너리가 아닌 파일에서
../passwords.txt에 나열된 모든 텍스트를 바꾸려면 다음 명령을 실행합니다.git-filter-repo --sensitive-data-removal --replace-text ../passwords.txt -
리포지토리의 기록에서 원하는 모든 항목을 제거했는지 다시 확인합니다.
-
이 기록 다시 쓰기로 인해 부정적인 영향을 받는 끌어오기 요청 수를 확인합니다. 아래 정보가 필요합니다.
$ grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs 4
-c를 삭제하여 영향을 받는 끌어오기 요청을 확인할 수 있습니다.
$ grep '^refs/pull/.*/head$' .git/filter-repo/changed-refs
refs/pull/589/head
refs/pull/602/head
refs/pull/604/head
refs/pull/605/head
해당 출력 결과에서 두 번째와 세 번째 슬래시 사이를 확인하면 풀 리퀘스트 번호를 알 수 있습니다.
영향을 받는 끌어오기 요청 수가 예상보다 많은 경우, 잘못된 효과 없이 이 클론을 삭제하고 다시 쓰기를 다시 실행하거나 중요한 데이터 제거를 중단하면 됩니다. 다음 단계로 이동하면 다시 쓰기는 되돌릴 수 없습니다.
-
리포지토리 상태가 만족스러우면 로컬 변경 사항을 강제 푸시하여 GitHub Enterprise Server 인스턴스의 리포지토리를 덮어쓰세요.
--force에 의해--mirror가 암시적으로 포함됨에도 불구하고, 여러분이 리포지토리를 정리했을 때 모든 브랜치, 태그, 참조를 강제로 업데이트하고, 다른 사용자가 해당 참조에서 수행한 모든 변경 사항을 폐기한다는 사실을 상기시키기 위해 아래에 명시적으로 포함하였습니다.git push --force --mirror origin이 명령은
refs/pull/로 시작하는 ref는 푸시하는 데 실패합니다. GitHub가 이를 읽기 전용으로 표시하기 때문입니다. 이러한 푸시 오류는 다음 섹션에서 다룰 예정입니다. 다른 참조에서도 푸시 오류가 발생한다면 해당 브랜치에 보호 설정이 적용되어 있을 가능성이 높습니다. 이 경우 브랜치 보호를 일시적으로 해제한 후 푸시를 다시 시도하세요.
refs/pull/(으)로 시작하는 ref만 업데이트에 실패할 때까지 반복하세요.
GitHub에서 데이터를 완전히 제거
git-filter-repo 중요한 데이터를 제거하고 변경 내용을 GitHub푸시한 후에는 데이터를 완전히 제거GitHub하려면 몇 가지 단계를 더 수행해야 합니다.
-
사이트 관리자에게 문의에 문의하고 다음 정보를 제공하세요.
-
문제의 소유자 및 리포지토리 이름(예: YOUR-USERNAME/YOUR-REPOSITORY)
-
이전 단계에서 찾은 영향을 받는 끌어오기 요청 수. 이는 사이트 관리자가 영향을 받는 정도를 사용자가 충분히 이해했는지 확인하는 데 사용됩니다.
-
git-filter-repo에서 보고된 첫 번째 변경된 커밋(출력에서NOTE: First Changed Commit(s)를 찾아보세요.) -
git-filter-repo 출력에서
NOTE: There were LFS Objects Orphaned by this rewrite가 나타나는 경우(첫 번째 변경된 커밋 바로 뒤), 분리된 LFS 개체를 언급하고 명명된 파일을 티켓에 업로드
-
PR 이외의 모든 참조를 성공적으로 정리하고 포크에 중요한 데이터에 대한 참조가 없는 경우 사이트 관리자는 다음을 수행합니다.
* GitHub에서 영향을 받은 PR을 모두 참조 해제하거나 삭제하세요.
* 서버에서 가비지 수집을 실행하여 스토리지에서 중요한 데이터를 제거합니다.
* 캐시된 뷰를 제거합니다.
* LFS 개체가 관련된 경우, 고아 상태의 LFS 개체를 삭제하거나 제거합니다.
사이트 관리자가 연결할 수 없는 Git 개체를 제거하는 방법에 대한 자세한 내용은 명령줄 유틸리티을 참조하세요. 사이트 관리자가 연결 가능한 커밋을 식별하는 방법에 대한 자세한 내용은 연결 가능한 커밋 식별을 참조하세요.
- 공동 작업자는 이전(오염된) 리포지토리 기록에서 만든 모든 분기에 대해 리베이스해야 하며 _병합_하면 안 됩니다. 하나의 병합 커밋은 방금 제거했던 오염된 기록의 일부 또는 전부를 다시 도입할 수 있습니다. 추가 단계가 더 필요할 수도 있습니다. 설명서의
git-filter-repo을 참조하세요.
연결할 수 있는 커밋 식별
리포지토리에서 원치 않거나 민감한 데이터를 완전히 제거하려면 데이터를 처음 도입한 커밋이 분기, 태그, 당겨받기 요청 및 포크에서 완전히 참조되지 않도록 해야 합니다. 단일 참조가 존재하는 한 가비지 컬렉션은 그 데이터를 메모리에서 제거할 수 없습니다.
SSH를 통해 어플라이언스에 연결할 때 다음 명령을 사용하여 기존 참조를 확인할 수 있습니다. 민감한 데이터를 처음 도입한 커밋의 SHA가 필요합니다.
ghe-repo OWNER/REPOSITORY -c 'git ref-contains COMMIT_SHA_NUMBER'
ghe-repo OWNER/REPOSITORY -c 'cd ../network.git && git ref-contains COMMIT_SHA_NUMBER'
이들 명령 중 어느 하나라도 결과가 나타난다면, 커밋이 가비지 컬렉션으로 완전히 제거되기 전에 먼저 해당 참조를 삭제해야 합니다. 두 번째 명령은 리포지토리의 포크에 존재하는 참조를 식별합니다(리포지토리에 포크가 없는 경우 실행을 건너뛸 수 있음).
refs/heads/또는refs/tags/(으)로 시작하는 결과는 각각 문제가 있는 커밋에 대한 참조가 여전히 포함된 분기 및 태그를 나타내며, 수정된 리포지토리에서 커밋이 완전히 정리되지 않았거나 강제 푸시되지 않았음을 의미합니다.refs/pull/또는refs/__gh__/pull(으)로 시작하는 결과는 문제가 있는 커밋을 참조하는 당겨받기 요청을 나타냅니다. 이러한 풀 리퀘스트들을 삭제해야만 커밋이 가비지 컬렉션될 수 있습니다. 사이트 관리자 대시보드의https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY/PULL_REQUESTS/<PULL-REQUEST-NUMBER>에서 풀 리퀘스트를 삭제할 수 있으며,<PULL-REQUEST-NUMBER>을(를) 풀 리퀘스트 번호로 대체하십시오.
어떤 포크에서든 참조가 발견되면 결과는 비슷하게 보이지만 refs/remotes/NWO/(으)로 시작됩니다. 이름으로 포크를 식별하려면 다음 명령을 실행하면 됩니다.
ghe-nwo NWO
포크된 리포지토리에서 민감한 데이터를 제거하려면, 먼저 포크의 복제본으로 이동한 후 정리된 리포지토리에서 데이터를 가져오고, 민감한 정보를 포함하고 있는 모든 분기와 태그를 정리된 리포지토리의 해당 분기와 태그 위에 리베이스해야 합니다. 또는 포크를 모두 삭제할 수 있으며 필요한 경우 루트 리포지토리 정리가 완료되면 리포지토리를 다시 포크할 수 있습니다.
커밋의 참조를 제거한 후 명령을 다시 실행하여 다시 확인합니다.
ref-contains 명령 중 하나에서 결과가 없는 경우 다음 명령을 실행하여 --prune 플래그를 사용하여 가비지 컬렉션을 실행하여 참조되지 않은 커밋을 제거할 수 있습니다.
ghe-repo-gc -v --prune OWNER/REPOSITORY
가비지 컬렉션으로 커밋이 성공적으로 제거되면 https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY에서 리포지토리의 사이트 관리자 대시보드로 이동하여 네트워크를 선택한 다음 Git 캐시 무효화를 클릭하여 캐시된 데이터를 제거할 수 있습니다.
향후 실수로 인한 커밋 방지
기여자가 실수로 커밋하지 못하게 하면 중요한 정보가 노출되지 않도록 방지할 수 있습니다. 자세한 내용은 조직에서 데이터 유출을 방지하기 위한 모범 사례을(를) 참조하세요.
공유해서는 안 되는 항목을 커밋하거나 푸시하지 않도록 하기 위해 수행할 수 있는 몇 가지 작업은 다음과 같습니다.
- git에서 추적하지 않아야 하는 파일에서 중요한 데이터를 찾을 수 있는 경우 해당 파일 이름을
.gitignore에 추가합니다(그리고 다른 개발자가 보호되도록 해당 변경 내용을.gitignore에 커밋하고 푸시해야 함). - 코드에서 비밀 하드코딩을 방지합니다. 환경 변수 또는 Azure Key Vault, AWS Secrets Manager 또는 HashiCorp Vault와 같은 비밀 관리 서비스를 사용하여 런타임에 비밀을 관리하고 삽입합니다.
- 커밋되거나 다른 곳으로 푸시되기 전에 중요한 데이터를 확인하는 사전 커밋 후크를 만들거나 git-secrets나 gitleaks와 같은 사전 커밋 후크에서 잘 알려진 도구를 사용합니다. (선택한 사전 커밋 후크를 설정하도록 각 공동 작업자에게 요청해야 합니다.)
- 시각적 프로그램(예: GitHub Desktopgitk )을 사용하여 변경 내용을 커밋합니다. 시각적 프로그램을 사용하면 일반적으로 각 커밋을 통해 추가, 삭제 및 수정할 파일을 정확하고 쉽게 확인할 수 있습니다.
- 대신 catch-all 명령
git add .와 명령줄의git commit -a을 사용하지 말고git add filename과git rm filename을 사용하여 파일을 개별적으로 스테이징하세요. - 각 파일 내에서 변경 내용을 개별적으로 검토하고 스테이징하는 데
git add --interactive를 사용합니다. - 커밋을 위해 스테이징한 변경 내용을 검토하는 데
git diff --cached를 사용합니다.git commit플래그를 사용하지 않는 한-a에서 생성되는 정확한 차이입니다. - 리포지토리에 대한 푸시 보호를 사용하도록 설정하여 하드 코딩된 비밀이 포함된 푸시가 코드베이스에 커밋되지 않도록 탐지하고 방지합니다. 자세한 내용은 푸시 보호을(를) 참조하세요.
추가 참고 자료
-
git-filter-repo매뉴얼 페이지, 특히 "토론" 섹션의 "중요한 데이터 제거" 하위 섹션 - Pro Git: Git 도구 - 기록 다시 쓰기
- 비밀 스캐닝