Aller au contenu
Sécuritégitcommitleaksecretstrufflehoggitleakspre-commitgpg

À l'aide ! j'ai "commit" mon mot de passe !

Venons en aide à tous ceux qui laisse fuiter des secrets via git (et doivent donc offrir des croissants) !

Que faire si j'ai commit mon mot de passe par erreur ?

En ce doux jour d'automne, Martin[1], tout sourire, s'apprête à boucler son projet. Il a tout vérifié, tout fonctionne, les tests unitaires passent sans souci et il vient d'avoir le "go" du "Project Owner" ! Il effectue donc un dernier "commit" sur son poste et "push" le tout sur le dépôt git du projet, dans la foulée. La journée s'annonce parfaite ! Tranquillement, et avec le sentiment du devoir accompli, Martin part donc chercher un café bien mérité !

[1] Dans un souci d'anonymat, les prénoms ont été modifiés.

Stupeur quand cinq minutes après, son collègue Michel, pas né de la dernière pluie, crie dans tout l'open-space : « Bah alors Martin ? on commit des "services accounts" sur GitHub ? J'en connais un qui va nous régaler avec des p'tits pains demain !! »

Martin ramène des croissants à toute son équipe !

Évidemment, toute ressemblance avec la réalité n'est absolument pas fortuite. Cette situation, beaucoup d'entre nous l'ont vécue, et malheureusement ça n'arrive pas qu'aux autres ! L'idée de cet article m'est justement venue parce que c'est arrivé à un collègue, plutôt aguerri et sensibilisé à ce problème.

Revenons à nos moutons et passons sur l'attitude questionnable de Michel. Attardons nous plutôt sur le fond : Martin vient d'exposer un "secret", dont l'accès est réservé à quelques personnes "accréditées", en clair sur internet, par le biais d'un dépôt git potentiellement public ou accessible à une bonne partie de l'entreprise. En l'occurrence, ici le "secret" est une clé permettant l'utilisation d'un compte de service. Pour rappel, on utilise très souvent sur les plateformes cloud, des comptes de service (appelés "service account" sur Google Cloud et AWS ou "service principal" sur Azure) qui permettent de donner des droits d'accès à des ressources pour des applications.

Exemple de fichier JSON contenant une clé d'accès à un compte de service Google (évidemment faux, pas la peine de vous en servir 😜)

Concrètement, cela signifie que n'importe qui ayant accès au dépôt git de Martin peut utiliser ce compte de service et avoir accès à des ressources auquel il ne devrait pas. Et ne soyez pas naïf : ne vous dites pas que personne ne tombera dessus. Il existe des tas de robots scannant perpétuellement les dépôts GitHub publics à la recherche de ces sésames. Et en cas d'intrusion, les conséquences peuvent être dramatiques.

Alors que faire si ça vous arrive ?

Si comme Martin, vous avez commis cette erreur, ou un de vos collègues (#C'EstPourUnAmi 🤫). Commencez par garder votre sang-froid, ce n'est pas le moment de paniquer. C'est parti pour un petit guide de survie :

1. Fermer le robinet !

Première chose, le PLUS URGENT est d'immédiatement désactiver ou effectuer une rotation du-dit "secret". Et oui, en cas d'inondation, on commence par fermer le robinet ! Si c'est une clé d'accès à un compte de service, on la révoque. Si c'est un mot de passe, on le change, etc.

Par exemple, sur Google Cloud, pour révoquer une clé de "service account", il faut se rendre dans l'interface d'IAM (Identity & Access Management)
Interface de gestion des clés de compte de service dans IAM sur Google Cloud

2. Supprimer le "commit" de l'historique

Ensuite, on peut supprimer le "commit" de l'historique du dépôt de sources. Nous venons de révoquer notre "secret", alors pour quelle raison supprimer le "commit", me rétorquerez-vous ? Certes, le "secret" n'est plus utilisable mais laisser des anciennes données sensibles, c'est comme mettre un panneau sur votre porte indiquant "ici, on met parfois la clé sous le paillasson". De plus, il y a peut-être également dans votre entreprise des scanners de failles et cela engendrerait des faux positifs. Donc mieux vaut effectuer cette étape.

Pour ce faire, il faut commencer par retrouver la signature du "commit" compromettant. On peut par exemple lister tous les "commits" ayant modifié le fichier impliqué en jouant la commande suivante dans une invite "bash" :
git log --follow [mon fichier] 
Résultat d'une commande "git log"
S'il s'agit du dernier "commit", on peut simplement revenir à l'état le précédant dans l'historique de git :
git reset --soft HEAD~1
git push --force
La première commande annule le "commit" sans modifier les fichiers concernés et la seconde réécrit l'historique distant sur le dépôt de manière forcée (à condition d'avoir les droits d'effectuer des "forced push").

La manière présentée ici est relativement simple et ne répond évidemment pas à tous les cas. Si nécessaire, on peut utiliser d'autres outils plus puissants tels que BFG Repo-Cleaner ou la commande git filter-repo.

Sachez qu'en outre, il est souvent impossible de supprimer l'entièreté d'un "commit" sur un dépôt distant ; la plupart du temps, on peut simplement faire disparaître toutes les références à sa signature[2]. De plus il faut considérer le cas où quelqu'un a eu le temps de récupérer vos dernières modifications avant que vous ne les ayez supprimées. Je vous invite donc à lire la documentation de votre hébergeur de sources, et notamment celle de GitHub à ce sujet.

[2] Sur GitHub, même si vous effectuez toutes ces étapes, vous pouvez retrouver le "commit" via sa signature exacte à condition de la connaître évidemment, en se rendant ici : https://github.com/[domaine]/[projet]/commit/[signature du commit]

3. Identifier les risques liés à la fuite

Reste une étape assez lourde : essayer d'identifier les potentiels risques liés à cette fuite et vérifier qu'il n'y a pas eu d'intrusion. Car oui, même si vous êtes allé très vite, il se peut qu'un robot sous les ordres d'une personne malveillante ait, comme évoqué précédemment, été très rapide et se soit déjà infiltré dans votre entreprise. Je vous conseille donc de prévenir et demander de l'aide aux équipes en charge de la sécurité si vous disposez d'un tel contingent. À défaut, il faut idéalement opérer une veille et éventuellement changer les autres clés ou mots de passe auxquels a pu avoir accès un potentiel hacker par rebond, ce qui s'apparente à une élévation de privilège[3].

[3] Voici la définition qu'en propose l'ANSSI dans son CyberDico 2024 : obtention de privilège supérieur par exploitation d’une vulnérabilité. Par exemple, si un utilisateur local accède à des droits normalement réservés à l’administrateur, il y a élévation de privilège. Une élévation de privilège est souvent recherchée par une personne malveillante lorsqu’elle a réussi à s’introduire sur un système d’information en usurpant l’identité d’un utilisateur légitime.

4. Ramener les croissants et les p'tits pain !

Bah oui, tout de même ! et n'oubliez pas de remercier Michel pour sa vigilance, peu importe que vous ne l'aimiez pas beaucoup et qu'il vous mène la vie dure ; il faut savoir être fair-play !

Martin est tellement sympa qu'il offre encore des croissants !

Mieux vaut prévenir que guérir

Ni Martin ni vous ne souhaitez vivre à nouveau cette situation. Alors maintenant que nous nous sommes sortis de ce mauvais pas, comment éviter que cela se reproduise ? Ça tombe bien que vous me posiez cette question, parce qu'il existe justement des techniques et des outils nous permettant de pallier nos erreurs d'inattention.

On peut agir de plusieurs façons et à plusieurs niveaux :

  • soit en adoptant des pratiques qui nous empêchent d'avoir des données sensibles directement dans nos dépôts de sources,
  • soit en filtrant au moment des "commits".

Chacune des solutions présentent des avantages indéniables et sont plus ou moins simples à mettre en œuvre. Elles peuvent aussi être combinées pour encore plus de sécurité !

Stocker les données sensibles ailleurs

Une bonne pratique consiste à ne pas stocker vos "secrets" dans vos dépôts git. Rien ne nous empêche de créer un répertoire ailleurs qui contiendra les clés de compte de service, les variables d'environnement, etc.

💡
En bonus, on peut même les chiffrer avec GPG, outil largement sous utilisé, pour plus de sécurité !
# Si vous souhaitez chiffrer vos données sensibles en local,
# voici une petite anti-sèche des commandes GPG

# créer rapidement une clé qui n'expire pas
gpg --quick-gen-key "Nom de la clé <email@bidon>" dsa3072 \ 
"description de la clé"

# chiffrer un fichier
cat mon_secret.json|gpg --encrypt --recipient email@bidon --yes \
--output mon_secret.json.gpg

# déchiffrer un fichier
gpg -d mon_secret.json.gpg

Une autre approche est de s'appuyer sur des outils comme Vault.

«Vault est une solution puissante qui permet de gérer de manière sécurisée les secrets et de les distribuer à des applications. »
https://www.sfeir.dev/cloud/simplifier-la-gestion-des-secrets-avec-vault-secret-operator/

Profitez-en si vous disposez déjà de cet outil ! Dans ce cas, vous stockerez tous vos secrets dans Vault, et donnerez à votre application, sur son environnement de déploiement des droits d'accès spécifiques au coffre-fort.

Filtrage au niveau des "commits"

Plusieurs méthodes existent pour filtrer le contenu d'un "commit" dont certaines que vous connaissez et employez déjà :

La plus simple, l'exclusion de certains fichiers (.gitignore)

Si vous êtes habitué à git, vous connaissez forcément le fichier .gitignore qui permet de ne pas indexer certains fichiers de la copie locale de votre dépôt afin qu'il ne figure pas dans votre dépôt distant. Sa fonction principale est d'éviter de polluer votre dépôt avec des fichiers parasites ou des fichiers sensibles. Pour gagner du temps lors de son initialisation, il existe des modèles prêts à l'emploi.

La première bonne pratique consiste donc à y référencer tous les fichiers contenant des "secrets" (tels que les clés de compte de service ou les fichiers .env par exemple).

N'oubliez pas que vous pouvez y utiliser des motifs de recherche !
Un beau paysage avec un fichier .gitignore devant

Une solution un peu moins connue est l'utilisation d'exclusion via le fichier .git/info/exclude qui fonctionne de la même façon que le .gitgignore à la différence qu'il ne sera pas présent sur le dépôt distant. Cela permet d'exclure des fichiers spécifiquement pour vous, sans affecter les autres contributeurs (assez pratique pour s'adapter aux habitudes de chacun).

💡
Astuce ! Vous pouvez aussi utiliser un .gitignore global pour tous vos dépôts git :
git config --global core.excludesfile ~/.gitignore

Aussi évitez l'utilisation de la commande git add . qui ajoute tous les fichiers de votre dépôt sans faire de tri. Mieux vaut sélectionner à la main chacun des fichiers que l'on souhaite modifier, ce qui est très facile avec la plupart des IDE récents.

Vérifier le contenu des fichiers avant d'effectuer un commit

On arrive à l'une des solutions les plus intéressantes ! Des bons samaritains ont développé des scanners qui interceptent tout ce qui s'apparente à des données sensibles (tels que des mots de passe, des jetons de session ("token" en anglais), ou des clés) au moment où vous vous apprêtez à "commit". La plupart d'entre elles se basent sur un outil nommé pre-commit[4], qui comme son nom l'indique, vous permet d'ajouter des actions avant chaque "commit".

[4] Rendez-vous sur le site web de pre-commit pour l'installer.

En recherchant sur internet, on trouve une palanquée d'outils pour détecter des "secrets" (detect-secret, GitGuardian, git-secrets, etc). Ici, je vous en propose deux : Gitleaks et TruffleHog, qui ont tous deux des versions "open source".

Gitleaks et sa simplicité d'utilisation

Cet outil est très simple à mettre en place ; à condition d'avoir docker installé sur son poste de travail, il vous suffit de créer un fichier nommé .pre-commit-config.yaml à la racine de votre dépôt comme suit :

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.2
    hooks:
      - id: gitleaks-docker
        name: Detect hardcoded secrets
        description: Detect hardcoded secrets using Gitleaks
        entry: zricethezav/gitleaks protect --verbose --redact --staged
        language: docker_image

.pre-commit-config.yaml pour appeler Gitleaks à chaque "commit"

Toujours à la racine de votre dépôt git, il ne reste plus qu'à installer cette action "pre-commit" via la commande :

pre-commit install

C'est tout ! Désormais il y aura systématiquement une vérification du contenu de vos fichiers, lorsque vous effectuerez un "commit", faisant potentiellement échouer ce dernier.

Voici le genre de chose que l'on obtient lorsqu'un "secret" est détecté.

La toute puissance de TruffleHog

Certes un peu plus compliqué à aborder, on peut faire encore mieux avec TruffleHog puisqu'il est bourré de fonctionnalités[5] et laisse place à tout un tas de personnalisations. De la même façon, on peut s'en servir via pre-commit et docker :

repos:
  - repo: local
    hooks:
      - id: trufflehog
        name: TruffleHog
        description: Detect secrets in your data.
        entry: bash -c 'docker run --rm -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --since-commit HEAD --only-verified --fail'
        language: system

.pre-commit-config.yaml pour appeler TruffleHog à chaque "commit"

[5] Je vous invite vivement à consulter sa documentation pour avoir une idée de son potentiel.

Cet outil introduit notamment une notion de secrets vérifiés et non vérifiés. Ceux qu'il qualifie de "vérifiés" sont en fait ceux qui permettent réellement d'accéder à des services en ligne. Oui ! oui ! Vous avez bien lu ! TruffleHog est capable d'interroger divers services pour vérifier si un secret est actif ou non !

Exemple de détection de secret "vérifié"
⚠️
Gardez toutefois en tête que ces deux outils ne sont pas une protection infaillible et ce pour deux raisons : il est nécessaire d'installer l'action "pre-commit" avant tout opération de commit et il se peut que certains secrets ne soient pas détectés.

Refuser un "commit" sur le dépôt distant s'il contient des données

Il est également possible avec GitHub et Gitlab de créer des "pre-received hooks", c'est-à-dire d'intercepter les "commits" au moment où l'on envoie ces derniers sur le dépôt distant ("git push"). Cependant, en ce qui concerne GitHub, il faut bénéficier de la version "Entreprise". C'est un peu plus complexe à mettre en œuvre et je vous invite donc à consulter les documentations afférentes car cela pourrait faire l'objet d'un article dédié.

Git server hooks | GitLab
GitLab product documentation.
About pre-receive hooks - GitHub Enterprise Server 3.10 Docs
Pre-receive hooks are scripts that run on the GitHub Enterprise Server appliance that you can use to implement quality checks.

Ce ne sont pas les solutions qui manquent

À travers l'histoire de Martin, j'espère vous avoir sensibilisé au réel danger que représentent les fuites de données sensibles pour les entreprises et leurs clients. Celles-ci sont si courantes (y compris par inadvertance), que des beaucoup de gens s'en sont souciés et ont développé des outils pour les prévenir. Désormais, il ne tient qu'à vous de vous en servir et de prêcher la bonne parole autour de vous ! Et si avec tout ça, vous offrez encore des croissants, c'est qu'au fond, vous êtes une belle personne !

Dernier