Sobre a remoção de dados confidenciais de um repositório
Ao alterar o histórico do repositório usando ferramentas como git-filter-repo, é fundamental entender as implicações. É necessário coordenar cuidadosamente com os colaboradores o processo de reescrever o histórico para que ele seja executado com êxito, e há uma série de efeitos colaterais que devem ser gerenciados.
É importante observar que, se os dados confidenciais que você precisa remover forem um segredo (por exemplo, senha/token/credencial), como costuma ser o caso, a primeira etapa será revogar e/ou alterar esse segredo. Após o segredo ser revogado ou alternado, ele não poderá mais ser usado para acesso, e isso pode ser suficiente para resolver o seu problema. Pode não ser necessário realizar as etapas extras de reescrever o histórico e remover o segredo.
Efeitos colaterais de reescrever o histórico
Reescrever o histórico traz alguns efeitos colaterais, que incluem:
-
**Alto risco de recontaminação**: infelizmente, é fácil voltar a efetuar push dos dados confidenciais para o repositório e fazer uma bagunça ainda maior. Se um colega desenvolvedor tiver um clone anterior à reescrita e, depois da reescrita, simplesmente executar `git pull` seguido de `git push`, os dados confidenciais retornarão. Eles precisam descartar o clone e clonar novamente ou seguir cuidadosamente várias etapas para limpar o clone deles primeiro. -
**Risco de perder o trabalho de outros desenvolvedores**: se outros desenvolvedores continuarem atualizando branches que contêm os dados confidenciais enquanto você estiver tentando limpá-los, você precisará refazer a limpeza ou descartar o trabalho deles. -
**Alteração dos hashes de commits**: A reescrita do histórico alterará os hashes dos commits que introduziram os dados sensíveis _e_ de todos os commits subsequentes. Qualquer ferramenta ou automação que exigir que hashes de commit não sejam alterados será interrompida ou terá problemas. -
**Desafios na proteção de filiais**: Se você tiver alguma proteção de filial que impeça a inserção forçada de dados, essas proteções precisarão ser desativadas (pelo menos temporariamente) para que os dados confidenciais sejam removidos. -
**Visualização de diferenças quebrada para solicitações de pull fechadas**: A remoção dos dados confidenciais exigirá a remoção das referências internas usadas para exibir a visualização de diferenças em solicitações de pull, portanto, você não poderá mais ver essas diferenças. Isso vale não apenas para a PR que introduziu os dados confidenciais, mas para qualquer PR baseada em uma versão do histórico posterior à mesclagem da PR com dados confidenciais (mesmo que essas PRs posteriores não tenham adicionado nem modificado nenhum arquivo com dados confidenciais). -
**Interação ineficiente com solicitações de pull abertas**: alterações nos SHAs de commits resultarão em um diff de PR diferente, e os comentários no diff antigo do PR podem ser invalidados e perdidos, o que pode causar confusão para autores e revisores. Recomendamos mesclar ou fechar todas as solicitações de pull abertas antes de remover arquivos do repositório. -
**Perda de assinaturas em commits e tags**: as assinaturas de commits ou tags dependem dos hashes de commit. Como os hashes de commit são modificados por reescritas de histórico, as assinaturas deixarão de ser válidas e muitas ferramentas de reescrita de histórico (incluindo o `git-filter-repo`) simplesmente as removerão. Na verdade, `git-filter-repo` também removerá as assinaturas de commits e as assinaturas de tags para commits anteriores à remoção dos dados sensíveis. (Tecnicamente, é possível contornar isso usando a opção `--refs` para `git-filter-repo` caso seja necessário, mas, nesse caso, você precisará ter cuidado para garantir que todas as referências com dados confidenciais em seu histórico sejam especificadas e que os commits que introduziram esses dados confidenciais no seu intervalo estejam incluídos.) -
**Direcionamento de outras pessoas para os dados confidenciais**: o Git foi criado com verificações de criptografia incorporadas nos identificadores de commit, de modo que pessoas mal-intencionadas não pudessem invadir um servidor e modificar o histórico sem serem detectadas. Isso é útil da perspectiva de segurança, mas da perspectiva de dados confidenciais significa que expurgar esses dados é um processo de coordenação muito elaborado. Significa ainda que, quando você modifica o histórico, usuários atentos com um clone existente observarão a divergência de históricos e poderão usar isso para localizar com rapidez e facilidade os dados confidenciais que ainda estão no clone que você removeu do repositório central.
Sobre a exposição de dados confidenciais
A remoção de dados confidenciais de um repositório envolve quatro etapas gerais:
- Reescrever o repositório localmente usando git-filter-repo
- Atualize o repositório em GitHub usando seu histórico reescrito localmente
- Coordenar com colegas para limpar outros clones existentes
- Impedir repetições e evitar futuros vazamentos de dados confidenciais
Se você apenas reescrever o histórico e forçar o push, commits com dados confidenciais ainda poderão ser acessados em outros lugares:
- Em quaisquer clones ou forks do seu repositório
- Diretamente por meio de seus hashes SHA-1 em visualizações em cache no GitHub
- Por meio de quaisquer solicitações de pull que façam referências a eles
Não é possível remover dados confidenciais dos clones de outros usuários do seu repositório; você precisará enviar as instruções contidas em Garantir que as outras cópias foram limpas: clones de colegas no manual de git-filter-repo para que eles mesmos façam isso. No entanto, você pode remover permanentemente as exibições armazenadas em cache e as referências aos dados confidenciais em pull requests no GitHub entrando em contato com o seu administrador do site.
Se a confirmação que introduziu os dados confidenciais existir em qualquer fork, ela continuará acessível lá. Você precisará coordenar com os proprietários dos forks, pedindo-lhes para remover os dados confidenciais ou excluir o fork completamente.
Considere essas limitações e desafios ao tomar a decisão de reescrever a história do repositório.
Como limpar um arquivo do histórico do repositório local usando o git-filter-repo
-
Instale a última versão da ferramenta
git-filter-repo. É preciso ter uma versão com o sinalizador--sensitive-data-removal, o que significa, no mínimo, a versão 2.47. Você pode instalar ogit-filter-repomanualmente ou usando um gerenciador de pacotes. Por exemplo, para instalar a ferramenta com HomeBrew, use o comandobrew install.brew install git-filter-repoPara obter mais informações, confira INSTALL.md no repositório
newren/git-filter-repo. -
Clone o repositório no computador local. Confira Clonar um repositório.
git clone https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY -
Navegue até o diretório de trabalho do repositório.
cd YOUR-REPOSITORY -
Execute um comando
git-filter-repopara limpar os dados confidenciais.Se quiser excluir um arquivo específico de todos os branches/tags/referências, execute o seguinte comando substituindo
PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATApelo caminho do Git para o arquivo que você deseja remover, não apenas o nome de arquivo (por exemplo,src/module/phone-numbers.txt):git-filter-repo --sensitive-data-removal --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATAImportante
Se o arquivo com os dados confidenciais existia em qualquer outro caminho (devido a ter sido movido ou renomeado), adicione um argumento
--pathextra para esse arquivo ou execute esse comando uma segunda vez nomeando o caminho alternativo.Se você quiser substituir todo o texto listado em
../passwords.txtem todos os arquivos não binários encontrados em qualquer lugar do histórico do repositório, execute o seguinte comando:git-filter-repo --sensitive-data-removal --replace-text ../passwords.txt -
Verifique se você removeu tudo o que queria do histórico do repositório.
-
Descubra quantas pull requests serão afetadas negativamente por essa reescrita de histórico. Você precisará dessas informações abaixo.
$ grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs 4Remova o
-cpara ver quais pull requests são afetadas:$ grep '^refs/pull/.*/head$' .git/filter-repo/changed-refs refs/pull/589/head refs/pull/602/head refs/pull/604/head refs/pull/605/headEssa saída inclui o número da solicitação de pull entre a segunda e a terceira barra. Se o número de pull requests afetadas for maior do que o esperado, você pode descartar esse clone sem efeitos nocivos e refazer a operação de reescrita ou abandonar a remoção dos dados confidenciais. Depois que você passar para a próxima etapa, a reescrita se tornará irreversível.
-
Quando estiver satisfeito com o estado do repositório, faça um push forçado das alterações locais para sobrescrever seu repositório em sua instância do GitHub Enterprise Server. Embora
--forceesteja implícito em--mirror, incluímos abaixo como um lembrete de que você está atualizando à força todos os ramificações, tags e refs, e descartando quaisquer alterações que outros possam ter feito nesses refs durante a limpeza do repositório.git push --force --mirror originEsse comando não efetuará push de nenhuma referência que começa com
refs/pull/, pois o GitHub marca as referências como somente leitura. Essas falhas de push serão abordadas na próxima seção. Em caso de falha no push de outras referências, é provável que você tenha a proteção de ramificação ativada para a ramificação em questão. Além disso, será necessário desativá-la temporariamente e refazer o push. Repita esta etapa até que as únicas falhas de atualização sejam as refs que começam comrefs/pull/.
Remover completamente os dados do GitHub
Depois de usar git-filter-repo para remover os dados confidenciais e efetuar push das alterações para o GitHub, você precisa executar mais algumas etapas para remover por completo os dados do GitHub.
-
Entre em contato com o seu administrador do site e informe o seguinte:
- O nome do proprietário e do repositório em questão (por exemplo, YOUR-USERNAME/YOUR-REPOSITORY).
- O número de pull requests afetadas, encontradas na etapa anterior. Isso é usado pelo administrador do site para verificar se você entende quanto será afetado.
- Os Primeiros Commits Alterados relatados pelo
git-filter-repo(procureNOTE: First Changed Commit(s)na saída). - Se
NOTE: There were LFS Objects Orphaned by this rewriteaparecer na saída do git-filter-repo (logo após o Primeiro Commit Alterado), mencione que você tinha objetos LFS órfãos e carregue o arquivo nomeado no tíquete também.
Se você tiver limpado com sucesso todas as referências que não sejam PRs e nenhum fork tiver referência aos dados confidenciais, o administrador do site vai:
- Desreferenciar ou excluir as PRs afetadas no GitHub.
- Realizar uma coleta de lixo no servidor para remover definitivamente os dados confidenciais do armazenamento.
- Remover as exibições armazenadas em cache.
- Se objetos LFS estiverem envolvidos, exclua e/ou limpe os objetos LFS órfãos.
Para saber mais sobre como os administradores do site podem remover objetos Git inacessíveis, confira Utilitários de linha de comando. Para obter mais informações sobre como os administradores do site podem identificar commits alcançáveis, consulte Identificando commits alcançáveis.
-
Os colaboradores devem realizar um rebase, e não uma fusão, de quaisquer ramificações que tenham criado a partir do histórico antigo (e corrompido) do seu repositório. Um commit de merge poderia reintroduzir o histórico antigo completo (ou parte dele) que você acabou de se dar ao trabalho de corrigir. Talvez eles também precisem realizar etapas adicionais; consulte Certifique-se de que outras cópias sejam limpas: clones de colegas no manual do
git-filter-repo.
Identificando commits alcançáveis
Para excluir completamente dados indesejados ou confidenciais de um repositório, o commit que introduziu os dados pela primeira vez precisa estar completamente sem referência em ramificações, tags, solicitações de pull e forks. Uma única referência em qualquer lugar impedirá que a coleta de lixo seja capaz de limpar os dados completamente.
Você pode verificar se há referências existentes usando os comandos a seguir quando conectado ao dispositivo via SSH. Você precisará do SHA do commit que originalmente introduziu os dados confidenciais.
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'
Se qualquer um desses comandos retornar algum resultado, você precisará remover essas referências antes que o commit possa ser coletado como lixo. O segundo comando identificará as referências que existem nos forks do repositório (se o repositório não tiver forks, você pode pular a execução).
- Resultados que começam com
refs/heads/ourefs/tags/indicam ramificações e tags, respectivamente, que ainda contêm referências ao commit problemático, sugerindo que o repositório modificado não foi completamente limpo do commit ou que ele não foi forçado a ser enviado (force push). - Os resultados que começam com
refs/pull/ourefs/__gh__/pullindicam pull requests que fazem referência ao commit problemático. Essas solicitações de pull precisam ser excluídas para permitir que o commit seja coletado como lixo. Uma pull request pode ser excluída no painel de administração do site emhttps://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY/PULL_REQUESTS/<PULL-REQUEST-NUMBER>, substituindo<PULL-REQUEST-NUMBER>pelo número da pull request.
Se forem encontradas referências em quaisquer bifurcações, os resultados serão semelhantes, mas começarão com refs/remotes/NWO/. Para identificar o fork pelo nome, você pode executar o comando a seguir.
ghe-nwo NWO
Para remover os dados confidenciais dos forks de um repositório, acesse um clone de um deles, busque no repositório limpo e, em seguida, efetue rebase de todas as ramificações e tags que contêm os dados confidenciais, além da ramificação ou tag relevante do repositório limpo. Como alternativa, os forks podem ser excluídos completamente e, se necessário, o repositório pode ser bifurcado novamente depois que a limpeza do repositório raiz for finalizada.
Depois de remover as referências do commit, execute novamente os comandos para verificar mais uma vez.
Se não houver resultados de nenhum dos comandos ref-contains, você poderá executar a coleta de lixo com o sinalizador --prune para remover os commits não referenciados executando o comando a seguir.
ghe-repo-gc -v --prune OWNER/REPOSITORY
Depois que a coleta de lixo tiver removido o commit com êxito, você desejará navegar até o painel de administração do site do repositório em https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY, selecionar Rede e clicar em Invalidar cache do Git para remover todos os dados armazenados em cache.
Evitar commits acidentais no futuro
Impedir que colaboradores façam commits acidentais pode ajudar a evitar que informações confidenciais sejam expostas. Para obter mais informações, confira Melhores práticas para evitar vazamentos de dados na sua organização.
Você pode realizar algumas ações para evitar commits ou pushes de itens que não devem ser compartilhados:
- Se os dados confidenciais provavelmente forem encontrados em um arquivo que não deve ser rastreado pelo git, adicione esse nome de arquivo a
.gitignore(e faça commit e push dessa alteração para.gitignorepara que outros desenvolvedores sejam protegidos). - Evite embutir segredos em código. Use variáveis de ambiente ou serviços de gerenciamento de segredos como Azure Key Vault, AWS Secrets Manager ou HashiCorp Vault para gerenciar e injetar segredos em runtime.
- Crie um gancho de pré-commit para verificar dados confidenciais antes de serem commitados ou enviados a qualquer repositório, ou utilize uma ferramenta conhecida em um gancho de pré-commit, como git-secrets ou gitleaks. (Peça a cada colaborador que configure o gancho pré-commit que você escolher.)
- Use um programa visual como o GitHub Desktop ou o gitk para realizar o commit das alterações. Nos programas visuais, geralmente é mais fácil ver exatamente quais arquivos serão adicionados, excluídos e modificados em cada commit.
- Evite os comandos catch-all
git add .egit commit -ana linha de comando: usegit add filenameegit rm filenamepara preparar os arquivos individualmente. - Use
git add --interactivepara revisar e preparar alterações individualmente em cada arquivo. - Use
git diff --cachedpara revisar as alterações que você preparou para commit. Essa é a comparação exata quegit commitproduzirá, desde que você não use o sinalizador-a. - Habilite a proteção por push para seu repositório para detectar e impedir que envios por push contendo segredos codificados sejam confirmados na sua base de código. Para saber mais, confira Sobre a proteção por push.
Leitura adicional
-
[ `git-filter-repo` página de manual](https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html), especialmente a subseção "Remoção de Dados Sensíveis" da seção "DISCUSSÃO". -
[Pro Git: Ferramentas do Git – Reescrita do histórico](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) -
[AUTOTITLE](/code-security/secret-scanning/introduction/about-secret-scanning)