Skip to main content

이 버전의 GitHub Enterprise는 다음 날짜에 중단되었습니다. 2024-09-25. 중요한 보안 문제에 대해서도 패치 릴리스가 이루어지지 않습니다. 더 뛰어난 성능, 향상된 보안, 새로운 기능을 위해 최신 버전의 GitHub Enterprise Server로 업그레이드합니다. 업그레이드에 대한 도움말은 GitHub Enterprise 지원에 문의하세요.

Removing sensitive data from a repository(리포지토리에서 중요한 데이터 제거)

만약 리포지토리를 복제한 모든 사용자와 신중하게 조정할 수 있고 부작용을 관리하려는 경우 중요한 데이터를 리포지토리 기록에서 제거할 수 있습니다.

리포지토리에서 중요한 데이터 제거 정보

git filter-repo와 같은 도구를 사용하여 리포지토리의 기록을 변경할 때는 그 의미를 이해하는 것이 중요합니다. 기록을 다시 작성하려면 공동 작업자와 신중하게 조정하여 성공적으로 실행해야 하며 관리해야 하는 여러 가지 부작용이 있습니다.

제거해야 하는 중요한 데이터가 비밀(예: 암호/토큰/자격 증명)인 경우와 마찬가지로 첫 번째 단계로 해당 비밀을 해지 및/또는 회전해야 합니다. 비밀이 해지되거나 회전되면 더 이상 액세스에 사용할 수 없으며 문제를 해결하는 데 충분할 수 있습니다. 기록을 다시 작성하고 비밀을 제거하는 추가 단계를 거치는 것은 보증되지 않을 수 있습니다.

다시 쓰기 기록의 부작용

기록을 다시 작성하면 다음과 같은 부작용이 발생할 수 있습니다.

  • 재확산 위험 높음: 안타깝지만, 중요한 데이터를 리포지토리에 다시 푸시하여 더 큰 혼란을 만들기 쉽습니다. 동료 개발자가 다시 쓰기 전의 복제본을 가지고 있고 다시 쓰기한 후 단순히 git pull을 실행된 후 git push를 실행하면 중요한 데이터가 반환됩니다. 복제본을 삭제하고 다시 복제하거나 여러 단계를 신중하게 진행하여 먼저 복제본을 정리해야 합니다.
  • 다른 개발자의 작업 손실 위험: 다른 개발자가 정리를 시도하는 동안 중요한 데이터가 포함된 분기를 계속 업데이트하는 경우 정리를 다시 실행하거나 작업을 취소해야 합니다.
  • 변경된 커밋 해시: 기록을 다시 작성하면 중요한 데이터 _ 및 _ 이후에 제공된 모든 커밋을 도입한 커밋의 해시가 변경됩니다. 변경되지 않는 커밋 해시에 의존하는 모든 도구나 자동화가 손상되거나 문제가 발생합니다.
  • 분기 보호 문제: 강제 푸시를 방지하는 분기 보호가 있는 경우 중요한 데이터를 제거하려면 해당 보호를 해제(적어도 일시적으로)해야 합니다.
  • 닫힌 끌어오기 요청에 대한 끊어진 Diff 뷰: 중요한 데이터를 제거하려면 끌어오기 요청에서 diff 뷰를 표시하는 데 사용되는 내부 참조를 제거해야 하므로 이러한 diff는 더 이상 볼 수 없습니다. 이는 중요한 데이터를 도입한 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 Enterprise Server의 캐시된 뷰에서 SHA-1 해시를 통해 직접
  • 이를 참조하는 끌어오기 요청을 통해

다른 사용자의 클론 또는 리포지토리 포크에서 중요한 데이터를 제거할 수는 없지만 사이트 관리자에게 문의.에 문의하여 GitHub Enterprise Server에 대한 끌어오기 요청에서 캐시된 뷰 및 중요한 데이터에 대한 참조를 제거할 수 있습니다.

중요한 데이터를 도입한 커밋이 모든 포크에 있는 경우 계속 액세스할 수 있습니다. 포크 소유자와 조정하여 중요한 데이터를 제거하거나 포크를 완전히 삭제하도록 요청해야 합니다.

리포지토리의 기록을 다시 작성하기로 결정할 때 이러한 제한 사항을 고려하세요.

git-filter-repo를 사용하여 리포지토리의 기록에서 파일 제거

Warning

변경 내용을 스태시한 후에 git filter-repo를 실행할 경우 다른 스태시 명령을 사용하여 변경 내용을 가져올 수 없습니다. git filter-repo를 실행하기 전에, 적용한 모든 변경 내용을 스태시 해제하는 것이 좋습니다. 스태시한 마지막 변경 내용 세트를 스태시 해제하려면 git stash show -p | git apply -R을 실행합니다. 자세한 내용은 Git 도구 - 스태시 및 정리를 참조하세요.

git filter-repo가 작동하는 방식을 설명하기 위해, 리포지토리 기록에서 중요한 데이터를 포함하는 파일을 제거한 다음 실수로 다시 커밋되지 않도록 .gitignore에 추가하는 방법을 보여 드리겠습니다.

  1. git filter-repo 도구의 최신 릴리스를 설치합니다. git-filter-repo는 수동으로 설치하거나 패키지 관리자를 사용하여 설치할 수도 있습니다. 예를 들어, HomeBrew를 사용하여 도구를 설치하려면 brew install 명령을 사용하세요.

    brew install git-filter-repo
    

    자세한 내용은 newren/git-filter-repo 리포지토리의 INSTALL.md를 참조하세요.

  2. 기록에 중요한 데이터가 포함된 리포지토리의 로컬 복사본이 아직 없다면 로컬 컴퓨터에 리포지토리를 복제합니다.

    $ git clone https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY
    > Initialized empty Git repository in /Users/YOUR-FILE-PATH/YOUR-REPOSITORY/.git/
    > remote: Counting objects: 1301, done.
    > remote: Compressing objects: 100% (769/769), done.
    > remote: Total 1301 (delta 724), reused 910 (delta 522)
    > Receiving objects: 100% (1301/1301), 164.39 KiB, done.
    > Resolving deltas: 100% (724/724), done.
    
  3. 리포지토리의 작업 디렉터리로 이동합니다.

    cd YOUR-REPOSITORY
    
  4. 아래의 명령에서 PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA(파일 이름이 아닌) 제거할 파일의 경로로 바꾸어서 실행합니다. 이 인수를 사용하면 다음과 같은 작업이 수행됩니다.

    • Git이 모든 분기와 태그의 전체 기록을 강제로 처리합니다(체크아웃하지 않음).

    • 지정된 파일과 그 결과로 생성된 빈 커밋을 제거합니다.

    • .git/config 파일에 저장된 일부 구성(예: 원격 URL)을 제거합니다. 이 파일은 나중에 복원할 수 있도록 미리 백업해두는 것이 좋습니다.

    • 기존 태그 덮어쓰기

        $ git filter-repo --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
        Parsed 197 commits
        New history written in 0.11 seconds; now repacking/cleaning...
        Repacking your repo and cleaning out old unneeded objects
        Enumerating objects: 210, done.
        Counting objects: 100% (210/210), done.
        Delta compression using up to 12 threads
        Compressing objects: 100% (127/127), done.
        Writing objects: 100% (210/210), done.
        Building bitmaps: 100% (48/48), done.
        Total 210 (delta 98), reused 144 (delta 75), pack-reused 0
        Completely finished after 0.64 seconds.
      

      Important

      중요한 데이터를 포함하는 파일이 이동되었거나 이름이 바뀌어서 다른 경로에 있다면 변경된 경로에 대해서도 이 명령을 실행해야 합니다.

  5. 리포지토리의 기록에서 원하는 모든 항목을 제거했는지 다시 확인합니다.

  6. git filter-repo 도구는 구성된 원격을 자동으로 제거합니다. git remote set-url 명령을 사용하여 원격을 복원합니다. 여기서 OWNERREPO를 리포지토리 세부 정보로 바꿉니다. 자세한 내용은 원격 리포지토리 관리을(를) 참조하세요.

    git remote add origin https://github.com/OWNER/REPOSITORY.git
    
  7. 리포지토리의 상태에 만족하고 적절한 원격을 설정했다면 로컬 변경 내용을 강제로 푸시하여 GitHub Enterprise Server 인스턴스에 리포지토리를 덮어쓰세요. 커밋 기록에서 중요한 데이터를 제거하려면 강제 푸시가 필요합니다.

    $ git push origin --force --all
    > Counting objects: 1074, done.
    > Delta compression using 2 threads.
    > Compressing objects: 100% (677/677), done.
    > Writing objects: 100% (1058/1058), 148.85 KiB, done.
    > Total 1058 (delta 590), reused 602 (delta 378)
    > To https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY.git
    >  + 48dc599...051452f main -> main (forced update)
    
  8. 태그된 릴리스에서 중요한 파일을 제거하려면 Git 태그에 대해서도 강제로 푸시해야 합니다.

    $ git push origin --force --tags
    > Counting objects: 321, done.
    > Delta compression using up to 8 threads.
    > Compressing objects: 100% (166/166), done.
    > Writing objects: 100% (321/321), 331.74 KiB | 0 bytes/s, done.
    > Total 321 (delta 124), reused 269 (delta 108)
    > To https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY.git
    >  + 48dc599...051452f main -> main (forced update)
    

GitHub에서 데이터 완전히 제거

git filter-repo를 사용하여 중요한 데이터를 제거하고 변경 내용을 GitHub Enterprise Server에 푸시한 후에는 GitHub Enterprise Server에서 데이터를 완전히 제거하려면 몇 가지 단계를 더 수행해야 합니다.

  1. 사이트 관리자에게 문의에 문의하여 GitHub Enterprise Server에 대한 끌어오기 요청에서 캐시된 뷰 및 중요한 데이터에 대한 참조를 제거하도록 요청합니다. 리포지토리의 이름 및/또는 제거해야 하는 커밋에 대한 링크를 제공하세요. 사이트 관리자가 연결할 수 없는 Git 개체를 제거하는 방법에 대한 자세한 내용은 명령줄 유틸리티을(를) 참조하세요. 사이트 관리자가 도달할 수 있는 커밋을 식별하는 방법에 대한 자세한 내용은 도달할 수 있는 커밋 식별하기를 참조하세요.

  2. 협력자에게 이전(오염된) 리포지토리 기록에서 만든 분기를 병합하지 ‘않고’ 다시 지정하도록 지시합니다.__ 하나의 병합 커밋은 방금 제거했던 오염된 기록의 일부 또는 전부를 다시 도입할 수 있습니다.

연결할 수 있는 커밋 식별

리포지토리에서 원치 않거나 민감한 데이터를 완전히 제거하려면 데이터를 처음 도입한 커밋이 분기, 태그, 당겨받기 요청 및 포크에서 완전히 참조되지 않도록 해야 합니다. 어디서든 단일 참조가 있으면 가비지 컬렉션이 데이터를 완전히 제거하지 못합니다.

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 Desktop 또는 gitk와 같은 시각적 프로그램을 사용하여 변경 내용을 커밋합니다. 시각적 프로그램을 사용하면 일반적으로 각 커밋을 통해 추가, 삭제 및 수정할 파일을 정확하고 쉽게 확인할 수 있습니다.
  • 대신 catch-all 명령 git add .와 명령줄의 git commit -a을 사용하지 말고 git add filenamegit rm filename을 사용하여 파일을 개별적으로 스테이징하세요.
  • 각 파일 내에서 변경 내용을 개별적으로 검토하고 스테이징하는 데 git add --interactive를 사용합니다.
  • 커밋을 위해 스테이징한 변경 내용을 검토하는 데 git diff --cached를 사용합니다. -a 플래그를 사용하지 않는 한 git commit에서 생성되는 정확한 차이입니다.
  • 리포지토리에 대한 푸시 보호를 사용하도록 설정하여 하드 코딩된 비밀이 포함된 푸시가 코드베이스에 커밋되지 않도록 탐지하고 방지합니다. 자세한 내용은 푸시 보호 정보을(를) 참조하세요.

추가 참고 자료