Skip to main content

Remover dados confidenciais de um repositório

Dados confidenciais poderão ser removidos do histórico de um repositório se você puder coordenar cuidadosamente com todos que o clonaram e estiver disposto a gerenciar os efeitos colaterais.

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 girar esse segredo. Após o segredo ser revogado ou girado, ele não poderá mais ser usado para acesso, e isso pode ser suficiente para resolver o 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. Ele precisa descartar o clone dele e clonar novamente ou realizar cuidadosamente várias etapas para limpar o clone dele 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.
  • Hashes de commit alterados: reescrever o histórico alterará os hashes dos commits que introduziram os dados confidenciais e de todos os commits que vieram depois. Qualquer ferramenta ou automação que exigir que hashes de commit não sejam alterados será interrompida ou terá problemas.
  • Desafios de proteção de branch: se você tiver proteções de branch que impedem pushes forçados, elas precisarão ser desativadas (pelo menos temporariamente) para que os dados confidenciais sejam removidos.
  • Exibição de comparação interrompida para pull requests fechadas: a remoção dos dados confidenciais exigirá a remoção das referências internas usadas para exibir a exibição de comparação em pull requests, de modo que você não poderá mais ver essas comparações. 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 insatisfatória com pull requests abertas: SHAs de commit alteradas resultarão em uma comparação de PR, e comentários sobre a comparação de PR antiga poderão 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.
  • Assinaturas perdidas em commits e tags: assinaturas em commits ou tags dependem de hashes de commit. Como hashes de commit são modificados por reescritas de histórico, as assinaturas deixariam de ser válidas e muitas ferramentas de reescrita de histórico (incluindo git filter-repo) simplesmente as removem. De fato, git filter-repo remove também assinaturas de commit e de tag para commits anteriores à remoção dos dados confidenciais também. (Tecnicamente, é possível contornar isso com a opção --refs para git filter-repo, quando necessário, mas nesse caso você precisará ter cuidado para garantir que especifique todas as referências com dados confidenciais em seu histórico e que incluam os commits que introduziram os dados confidenciais em seu intervalo).
  • Levar outras pessoas diretamente aos dados confidenciais: o Git foi projetado com verificações criptográficas incorporadas a identificadores de commit para que indivíduos maldosos não pudessem invadir um servidor e modificar o histórico sem serem notados. 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
  • Atualizar o repositório no 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 efetuar o push forçado dele, commits com dados confidenciais ainda poderão estar acessíveis em outro lugar:

  • Em quaisquer clones ou forks do seu repositório
  • Diretamente por meio de seus hashes SHA-1 em exibições em cache em GitHub Enterprise Server
  • Por meio de pull requests que façam referência

Não é possível remover dados confidenciais dos clones de outros usuários do seu repositório, mas é possível remover permanentemente as exibições e as referências em cache aos dados confidenciais nas solicitações de pull do GitHub Enterprise Server 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.

Removendo um arquivo do histórico do repositório usando git-filter-repo

Warning

Ao executar git filter-repo após o stash das alterações, você não poderá recuperar as alterações com outros comandos stash. Antes de executar git filter-repo, é recomendado cancelar o stash das alterações feitas. Para cancelar o stash do último conjunto de alterações com stash, execute git stash show -p | git apply -R. Para obter mais informações, confira Ferramentas do Git – Stash e limpeza.

Para ilustrar como o git filter-repo funciona, mostraremos como remover o arquivo que contém dados confidenciais do histórico do repositório e adicioná-lo ao .gitignore para garantir que ele não sofra um novo commit acidental.

  1. Instale a última versão da ferramenta git filter-repo. Você pode instalar o git-filter-repo manualmente ou usando um gerenciador de pacotes. Por exemplo, para instalar a ferramenta com HomeBrew, use o comando brew install.

    brew install git-filter-repo
    

    Para obter mais informações, confira INSTALL.md no repositório newren/git-filter-repo.

  2. Se você ainda não tem uma cópia local do repositório que contém dados confidenciais no histórico, clone o repositório no computador local.

    $ 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. Navegue até o diretório de trabalho do repositório.

    cd YOUR-REPOSITORY
    
  4. Execute o comando a seguir, substituindo PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA pelo caminho para o arquivo que deseja remover, não apenas o nome do arquivo. Esses argumentos vão:

    • Forçar o Git a processar todo o histórico de cada branch e marca, mas não fazer check-out

    • Remover o arquivo especificado, bem como todos os commits vazios gerados como resultado

    • Remova algumas configurações, como a URL remota, armazenada no arquivo .git/config. É interessante fazer backup desse arquivo antes para que ele possa ser restaurado mais tarde.

    • Substituir as marcas existentes

        $ 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

      Se o arquivo que contém dados confidenciais existia em algum outro caminho (porque foi movido ou renomeado), você também precisa executar esse comando nesses caminhos.

  5. Verifique se você removeu tudo o que queria do histórico do repositório.

  6. A ferramenta git filter-repo removerá automaticamente os itens remotos configurados. Use o comando git remote set-url para restaurar seus itens remotos, substituindo OWNER e REPO pelos detalhes de seu repositório. Para saber mais, confira Gerenciar repositórios remote.

    git remote add origin https://github.com/OWNER/REPOSITORY.git
    
  7. Quando estiver satisfeito com o estado do repositório e tiver definido o repositório remoto apropriado, efetue o push forçado das alterações locais para substituir seu repositório em sua instância do GitHub Enterprise Server. É necessário um push forçado para remover dados confidenciais do seu histórico de commit.

    $ 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. Para remover o arquivo confidencial das versões marcadas, você também precisará forçar o push das marcas do 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)
    

Remover completamente os dados de GitHub

Depois de usar git filter-repo para remover os dados confidenciais e efetuar push das alterações para o GitHub Enterprise Server, você precisa executar mais algumas etapas para remover por completo os dados do GitHub Enterprise Server.

  1. Entre em contato com o seu administrador do site e solicite a remoção de exibições em cache e referências aos dados confidenciais em solicitações de pull no GitHub Enterprise Server. Forneça o nome do repositório e/ou um link para o commit que você precisa remover. Para saber mais sobre como os administradores de 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 acessíveis, confira Identificar commits acessíveis.

  2. Diga aos colaboradores para trocar a base (não fazer a mesclagem) dos branches que eles criaram fora do histórico do repositório antigo (afetado). Um commit de merge poderia reintroduzir o histórico antigo completo (ou parte dele) que você acabou de se dar ao trabalho de corrigir.

Identificando commits alcançáveis

Para remover totalmente dados indesejados ou confidenciais de um repositório, o commit que introduziu os dados pela primeira vez precisa ter a referência totalmente removida em branches, tags, solicitações de pull e bifurcações. 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 a confirmação possa ser coletada como lixo com êxito. O segundo comando identificará as referências que existem nos forks do repositório (se o repositório não tiver bifurcações, você poderá pular a execução).

  • Os resultados que começam com refs/heads/ ou refs/tags/ indicam ramificações e tags, respectivamente, que ainda contêm referências ao commit ofensivo, sugerindo que o repositório modificado não foi totalmente limpo do commit ou que não foi forçado por push.
  • Os resultados que começam com refs/pull/ ou refs/__gh__/pull indicam pull requests que fazem referência à confirmação ofensiva. Essas pull requests precisam ser excluídas para permitir que o commit seja coletado como lixo. Uma solicitação de pull pode ser excluída no painel de administração do site em https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY/PULL_REQUESTS/<PULL-REQUEST-NUMBER> com a substituição de <PULL-REQUEST-NUMBER> pelo número da pull request.

Se forem encontradas referências em qualquer fork, 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

Dados confidenciais podem ser removidos dos forks de um repositório acessando um clone de um, buscando do repositório limpo e, em seguida, efetuando rebase de todos os branches e tags que contêm os dados confidenciais sobre o branch ou tag relevante do repositório limpo. Como alternativa, as bifurcações podem ser excluídas completamente e, se necessário, o repositório pode ser bifurcado novamente assim que a limpeza do repositório raiz for concluída.

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 saber mais, confira Melhores práticas para evitar vazamentos de dados na sua organização.

Você pode fazer algumas coisas para evitar fazer commit ou push de coisas que não devem ser compartilhadas:

  • 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 .gitignore para que outros desenvolvedores sejam protegidos).
  • Evite embutir segredos em código. Use variáveis de ambiente ou serviços de gerenciamento de segredos como o Azure Key Vault, o AWS Secrets Manager ou o HashiCorp Vault para gerenciar e injetar segredos no runtime.
  • Crie um gancho pré-commit para verificar se há dados confidenciais antes que seja feito commit ou push deles para qualquer lugar, ou use uma ferramenta conhecida em um gancho pré-commit, como git-secrets ou gitleaks. (Peça a cada colaborador para configurar o gancho pré-commit escolhido por você.)
  • Use um programa visual como o GitHub Desktop ou o gitk para fazer 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 . e git commit -a na linha de comando: use git add filename e git rm filename para preparar os arquivos individualmente.
  • Use git add --interactive para revisar e preparar alterações individualmente em cada arquivo.
  • Use git diff --cached para revisar as alterações que você preparou para commit. Essa é a comparação exata que git commit produzirá, 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