À propos de la suppression de données sensibles dans un référentiel
Lorsque vous modifiez l’historique de votre référentiel à l’aide d’outils comme git filter-repo
, il est essentiel de comprendre les implications. L’historique de réécriture nécessite une coordination minutieuse avec les collaborateurs pour s’exécuter avec succès et a un certain nombre d’effets secondaires qui doivent être gérés.
Il est important de noter que si les données sensibles que vous devez supprimer sont secrètes (par exemple, un mot de passe, un jeton ou un identifiant), comme c’est souvent le cas, vous devez d’abord révoquer et/ou renouveler ce secret. Une fois le secret révoqué ou renouvelé, il ne peut plus être utilisé pour l’accès, et cela peut suffire à résoudre votre problème. Dans ce cas, il peut ne pas être nécessaire d’effectuer les étapes supplémentaires de réécriture de l’historique et de suppression du secret.
Effets secondaires de la réécriture de l’historique
Réécrire l’historique comporte de nombreux effets secondaires, notamment :
- Risque élevé de recontamination : malheureusement, il est facile d’envoyer à nouveau les données sensibles dans le référentiel et d’aggraver la situation. Si un autre développeur dispose d’un clone antérieur à votre réécriture, et qu’après votre réécriture, il exécute simplement
git pull
suivi degit push
, les données sensibles réapparaîtront. Il doit soit abandonner son clone et le recréer, soit suivre attentivement plusieurs étapes pour nettoyer d’abord son clone. - Risque de perdre le travail d'autres développeurs : si d’autres développeurs continuent à mettre à jour des branches contenant des données sensibles pendant que vous essayez de procéder au nettoyage, vous devrez soit recommencer le nettoyage, soit abandonner leur travail.
- Modification des hachages de commit : la réécriture de l’historique modifiera les hachages des commits qui ont introduit les données sensibles et de tous les commits ultérieurs. Tout outil ou automatisation qui dépend de l’invariabilité des hachages de commit sera défaillant ou présentera des problèmes.
- Défis liés à la protection des branches : si des protections de branche empêchent le passage en force, ces protections devront être désactivées (au moins temporairement) pour que les données sensibles soient supprimées.
- Vue diff cassée pour les demandes de tirage (pull request) fermées : la suppression des données sensibles nécessitera la suppression des références internes utilisées pour l’affichage de la vue diff dans les demandes de tirage. Vous ne serez donc plus en mesure de voir ces différences. Cela est vrai non seulement pour la demande de tirage qui a introduit les données sensibles, mais aussi pour toutes les demandes de tirage qui s’appuient sur une version de l’historique postérieure à la fusion de la demande de tirage contenant les données sensibles (même si ces demandes de tirage ultérieures n’ont pas ajouté ou modifié de fichier contenant des données sensibles).
- Mauvaise interaction avec les demandes de tirage ouvertes : les modifications des SHA de commit se traduiront par un diff de demande de tirage (PR) différent, et les commentaires sur l’ancien diff de PR peuvent être invalidés et perdus, ce qui peut être source de confusion pour les auteurs et les réviseurs. Nous vous recommandons de fusionner ou de fermer toutes les demandes de tirage ouvertes avant de supprimer des fichiers de votre dépôt.
- Perte des signatures sur les commits et les balises : les signatures des commits ou des balises dépendent des hachages de commit. Comme les hachages de commit sont modifiés par les réécritures de l’historique, les signatures ne seraient plus valides et de nombreux outils de réécriture de l’historique (y compris
git filter-repo
) supprimeraient simplement les signatures. En fait,git filter-repo
supprimera également les signatures de commit et les signatures de balise pour les commits antérieurs à la suppression des données sensibles. (Techniquement, il est possible de contourner ce problème en ajoutant l’option--refs
àgit filter-repo
si nécessaire, mais vous devrez alors veiller à spécifier tous les refs qui contiennent des données sensibles dans leur historique et qui incluent les commits qui ont introduit les données sensibles dans votre plage.) - Diriger les autres directement vers les données sensibles : Git a été conçu avec des contrôles cryptographiques intégrés dans les identificateurs de commit afin que des individus malveillants ne puissent pas pénétrer dans un serveur et modifier l’historique sans être détectés. C’est utile du point de vue de la sécurité, mais du point de vue des données sensibles, cela signifie que leur suppression est un processus de coordination très complexe. Cela signifie également que lorsque vous modifiez l’historique, les utilisateurs prudents possédant un clone existant remarqueront la divergence de l’historique et pourront l’utiliser pour trouver rapidement et facilement les données sensibles encore présentes dans leur clone et que vous avez supprimées du référentiel central.
À propos de l’exposition aux données sensibles
La suppression des données sensibles d'un référentiel comporte quatre étapes principales :
- Réécrire le référentiel localement à l’aide de git-filter-repo
- Mettre à jour le référentiel sur GitHub à l’aide de votre historique réécrit localement
- Se coordonner avec des collègues pour nettoyer d’autres clones qui existent
- Empêcher les répétitions et éviter les futures fuites de données sensibles
Si vous réécrivez seulement votre historique et que vous le poussez de force, les commits contenant des données sensibles peuvent toujours être accessibles ailleurs :
- Dans tous les clones ou duplications de votre référentiel
- Directement via leurs hachages SHA-1 dans les vues mises en cache sur GitHub Enterprise Server
- Par le biais de toutes les demandes de tirage qui les référencent
Vous ne pouvez pas supprimer les données sensibles de clones d’autres utilisateurs de votre référentiel, mais vous pouvez supprimer définitivement les vues en cache et les références aux données sensibles dans les demandes de tirage sur GitHub Enterprise Server en contactant le votre administrateur de site.
Si le commit qui a introduit les données sensibles existe dans une duplication, elles continueront d'y être accessibles. Vous devrez vous coordonner avec les propriétaires des duplications, en leur demandant de supprimer les données sensibles ou de supprimer entièrement la duplication.
Tenez compte de ces limitations et de ces problématiques dans votre décision de réécrire l’historique de votre dépôt.
Supprimer définitivement un fichier de l’historique de votre référentiel à l’aide de git-filter-repo
Warning
Si vous exécutez git filter-repo
après avoir remisé (stash) des modifications, vous ne pourrez pas récupérer vos modifications avec d’autres commandes stash. Avant d’exécuter git filter-repo
, nous vous recommandons de déremiser les modifications que vous avez apportées. Pour déremiser le dernier ensemble de modifications que vous avez remisées, exécutez git stash show -p | git apply -R
. Pour plus d’informations, consultez Git Tools - Stashing and Cleaning (Outils Git - remiser et nettoyer).
Pour illustrer le fonctionnement de git filter-repo
, nous allons vous montrer comment supprimer votre fichier contenant des données sensibles de l’historique de votre dépôt et comment l’ajouter à .gitignore
pour garantir qu’il n’est pas recommité accidentellement.
-
Installez la dernière version de l’outil git filter-repo. Vous pouvez installer
git-filter-repo
manuellement ou en utilisant un gestionnaire de package. Par exemple, pour installer l’outil avec HomeBrew, utilisez la commandebrew install
.brew install git-filter-repo
Pour plus d’informations, consultez INSTALL.md dans le dépôt
newren/git-filter-repo
. -
Si vous ne disposez pas déjà d’une copie locale de votre dépôt avec des données sensibles dans son historique, clonez le dépôt sur votre ordinateur 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.
-
Accédez au répertoire de travail du dépôt.
cd YOUR-REPOSITORY
-
Exécutez la commande suivante en remplaçant
PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
par le chemin du fichier que vous voulez supprimer, et pas seulement par son nom de fichier. Ces arguments vont :-
Forcer Git à traiter, mais pas à extraire, l’historique complet de chaque branche et chaque étiquette
-
Supprimer le fichier spécifié ainsi que tous les commits générés en tant que résultat
-
Supprimer certaines configurations comme l’URL distante, stockées dans le fichier .git/config Vous pouvez sauvegarder ce fichier avant pour le restaurer ultérieurement.
-
Remplacer vos étiquettes existantes
$ 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
Si le fichier avec des données sensibles était utilisé dans d’autres chemins (en raison du fait qu’il a été déplacé ou renommé), vous devez également exécuter cette commande sur ces chemins.
-
-
Vérifiez bien que vous avez supprimé tout ce que vous vouliez de l’historique de votre référentiel.
-
L’outil
git filter-repo
supprime automatiquement vos dépôts distants configurés. Utilisez la commandegit remote set-url
pour restaurer vos dépôts distants, en remplaçantOWNER
etREPO
grâce aux détails de votre référentiel. Pour plus d’informations, consultez « Création de dépôt distants ».git remote add origin https://github.com/OWNER/REPOSITORY.git
-
Une fois satisfait(e) de l’état de votre référentiel, et que vous avez défini le référentiel distant approprié, effectuez une poussée forcée de vos modifications locales pour écraser votre référentiel sur votre instance GitHub Enterprise Server. Une poussée forcée est nécessaire pour supprimer les données sensibles de votre historique des commits.
$ 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)
-
Pour supprimer le fichier sensible de vos versions étiquetées, vous devez également effectuer un envoi (push) forcé sur vos étiquettes 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)
Suppression complète des données de GitHub
Après avoir utilisé git filter-repo
pour supprimer les données sensibles et envoyé (push) vos modifications sur GitHub Enterprise Server, vous devez effectuer quelques étapes supplémentaires pour supprimer entièrement les données de GitHub Enterprise Server.
-
Contactez votre administrateur de site, et demandez-leur de supprimer les vues en cache et les références aux données sensibles dans les demandes de tirage sur GitHub Enterprise Server. Indiquez le nom du dépôt et/ou un lien vers le commit que vous devez supprimer. Pour plus d’informations sur la façon dont les administrateurs de site peuvent supprimer des objets Git inaccessibles, consultez « Utilitaires de ligne de commande ». Pour plus d'informations sur la façon dont les administrateurs de site peuvent identifier les commits atteignables, voir Identifier les commits atteignables
-
Dites à vos collaborateurs de rebaser et non de fusionner les branches qu’ils ont créées à partir de l’ancien historique de votre dépôt (compromis). Un commit de fusion pourrait réintroduire une partie ou l’ensemble de l’histoire compromis que vous vous êtes donné la peine de supprimer.
Identification des validations accessibles
Pour supprimer entièrement les données indésirables ou sensibles d’un référentiel, la validation qui a introduit les données doit d’abord être complètement non référencée dans les branches, les balises, les demandes de tirage et les duplications. Une référence unique n’importe où empêche le garbage collection de pouvoir vider complètement les données.
Vous pouvez rechercher des références existantes à l’aide des commandes suivantes lors de la connexion à l’appliance via SSH. Vous aurez besoin de SHA de la validation qui a introduit initialement les données sensibles.
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'
Si l’une de ces commandes retourne des résultats, vous devez supprimer ces références avant que la validation puisse être correctement récupérée. La deuxième commande identifie les références qui existent dans les duplications du référentiel (si le référentiel n’a pas de fourche, vous pouvez ignorer son exécution).
- Les résultats commençant
refs/heads/
par ourefs/tags/
indiquant des branches et des balises, qui contiennent toujours des références à la validation incriminée, suggèrent que le référentiel modifié n’a pas été entièrement nettoyé de la validation, ou qu’il n’a pas été envoyé par force. - Résultats commençant par
refs/pull/
ourefs/__gh__/pull
indiquant des demandes de tirage qui font référence à la validation incriminé. Ces demandes de tirage doivent être supprimées pour permettre à la validation d’être récupérée par le garbage collect. Une demande de tirage (pull request) peut être supprimée dans le tableau de bord administrateur du site àhttps://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY/PULL_REQUESTS/<PULL-REQUEST-NUMBER>
, en remplaçant<PULL-REQUEST-NUMBER>
par le numéro de demande de tirage.
Si des références sont trouvées dans des fourches, les résultats ressemblent, mais commencent par refs/remotes/NWO/
. Pour identifier le dupliquer (fork) par nom, vous pouvez exécuter la commande suivante.
ghe-nwo NWO
Les données sensibles peuvent être supprimées des fourches d’un référentiel en allant sur un clone de celui-ci, en récupérant les données du référentiel nettoyé, puis en replaçant toutes les branches et les balises qui contiennent les données sensibles au-dessus de la branche ou de la balise concernée du référentiel nettoyé. Vous pouvez également supprimer complètement les duplications et, si nécessaire, le dépôt peut être redimensionné une fois le nettoyage du référentiel racine terminé.
Une fois que vous avez supprimé les références de la validation, réexécutez les commandes pour double-vérifier.
S’il n’existe aucun résultat de l’une des commandes ref-contains
, vous pouvez exécuter garbage collection avec l’indicateur --prune
pour supprimer les validations non référencées en exécutant la commande suivante.
ghe-repo-gc -v --prune OWNER/REPOSITORY
Une fois que le garbage collection a correctement supprimé la validation, vous souhaiterez accéder au tableau de bord d’administration du site du référentiel sur https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY
, sélectionnez Réseau, puis cliquez sur Invalider le cache Git pour supprimer les données en cache.
Éviter les commits accidentels à l’avenir
En empêchant les contributeurs d’effectuer des commits accidentels, vous contribuez à empêcher l’exposition des informations sensibles. Pour plus d'informations, voir Bonnes pratiques pour empêcher les fuites de données dans votre organisation.
Il existe plusieurs moyens d’éviter de commettre ou d'envoyer (push) des éléments qui ne devraient pas être partagés :
- Si les données sensibles sont susceptibles de se trouver dans un fichier qui ne devrait pas être suivi par git, ajoutez ce nom de fichier à
.gitignore
(et assurez-vous de commiter et d’envoyer (push) cette modification à.gitignore
pour que les autres développeurs soient protégés). - Évitez de coder en dur des secrets dans le code. Utilisez des variables d’environnement ou des services de gestion des secrets comme Azure Key Vault, AWS Secrets Manager ou HashiCorp Vault pour gérer et injecter des secrets au moment de l’exécution.
- Créez un hook de pré-commit pour vérifier les données sensibles avant qu’elles ne soient commitées ou envoyées (push) n’importe où, ou utilisez un outil bien connu dans un hook de pré-commit comme git-secrets ou gitleaks. (Veillez à demander à chaque collaborateur de mettre en place le hook de pré-commit que vous avez choisi.)
- Utilisez un programme visuel comme GitHub Desktop ou gitk pour commiter les modifications. Généralement, les programmes visuels permettent de voir plus facilement les fichiers exacts qui seront ajoutés, supprimés et modifiés avec chaque commit.
- Évitez les commandes génériques
git add .
etgit commit -a
dans la ligne de commande : utilisez plutôtgit add filename
etgit rm filename
pour indexer les fichiers individuellement. - Utilisez
git add --interactive
pour vérifier et indexer les modifications dans chaque fichier. - Utilisez
git diff --cached
pour vérifier les modifications que vous avez indexées pour le commit. Il s’agit de la différence exacte quegit commit
produira tant que vous n’utilisez pas l’indicateur-a
. - Activez la protection Push pour votre référentiel afin de détecter et d’empêcher les envois qui contiennent des secrets codés en dur d’être validés dans votre codebase. Pour plus d’informations, consultez « À propos de la protection push ».
Pour aller plus loin
- Page man
git filter-repo
- Pro Git : Git Tools - Rewriting History (Outils Git - Réécriture de l’historique)
- À propos de l’analyse des secrets