Construire une détection de secrets fiable : défis et exemple appliqué

Après avoir examiné en profondeur la façon dont les secrets se dispersent et pourquoi les secrets dans Git sont un tel problème, voici comment les algorithmes peuvent détecter les secrets et ce que l'on peut apprendre en analysant des milliards de commits.

Un petit rappel sur la dispersion des secrets

Les secrets, comme les clés d'API, les informations d'identification et les certificats de sécurité, sont ce qu'il y a de plus précieux pour une organisation. Ils permettent d'accéder aux systèmes et aux données les plus sensibles. Mais nous sommes confrontés à un paradoxe avec les secrets : ils doivent être à la fois étroitement contrôlés et sécurisés, mais ils doivent aussi être largement distribués aux membres de l'équipe, aux applications et à l'infrastructure. Il peut en résulter une dispersion des secrets : sauvegardés localement, partagés par des systèmes de messagerie ou des wikis internes, codés en dur dans le code source...

Ces secrets peuvent être enfouis profondément dans des systèmes tels que l'historique de la ligne de commande de votre serveur le plus utilisé, les registres d'applications ou l'historique git, ce qui rend leur détection très difficile.

Pourquoi la détection des secrets est un défi

À première vue, le défi de la détection des secrets peut sembler évident : il s'agit d'identifier des modèles spécifiques de clés connues dans le code. La réalité est bien plus complexe. Chez GitGuardian, nous avons lancé nos premiers algorithmes de détection en 2017 avec à peu près le même optimisme. Depuis lors, nos algorithmes ont analysé chaque commit public jamais effectué sur GitHub. Pour vous donner un ordre de grandeur, cela représente près d'un milliard de commits par an, et aujourd'hui nous détectons plus d'un million de secrets chaque année. Cette quantité de données, combinée à des boucles de rétroaction, a permis de faire des découvertes vraiment fascinantes sur la façon dont les secrets se retrouvent dans le code et sur les méthodes d’identification d'un vrai positif.

La détection des secrets est probabiliste, c'est-à-dire qu'il n'est pas toujours possible de déterminer ce qui est un vrai secret (ou un vrai positif). Nous devons tenir compte de la probabilité d'un vrai secret en fonction de différents paramètres et indicateurs. Certains secrets ont des modèles fixes, mais la plupart n'en ont pas, ils sont de longueurs différentes, utilisent des jeux de caractères variés et apparaissent dans des contextes différents. Il est donc extrêmement difficile de capter avec précision tous les vrais secrets sans capter également des faux positifs.

À un moment donné, il faut tracer une ligne de démarcation qui tienne compte du coût d'un secret non détecté (un faux négatif) et le comparer au coût d’un trop grand nombre de faux positifs. À quel moment l'outil perd-il son efficacité ?

Comment détecter les secrets

La détection de secrets est un processus en deux étapes. On commence par la recherche des secrets potentiels. L'étape suivante, et l'aspect le plus délicat de la détection de secrets, est de filtrer efficacement ces résultats pour exclure les faux positifs, grâce à différents indicateurs. Nous allons d'abord passer en revue les méthodes couramment utilisées pour découvrir et valider les types de secrets, puis utiliser GitGuardian comme étude de cas pour montrer ce que nous faisons de plus.

Etape 1 : Détecter les secrets potentiels

Il existe actuellement deux approches bien connues pour détecter de potentiels secrets dans le code : la détection de chaînes à forte entropie d'une part, et l'utilisation d'expressions régulières (regex) pour détecter des modèles connus dans les secrets d'autre part (nous appelons cela des secrets pré- ou post-fixés).

1. Détection des chaînes à haute entropie

Les chaînes à haute entropie sont des chaînes générées par ordinateur qui utilisent les mathématiques pour créer une chaîne de caractères qui semble aléatoire. Plus la chaîne est aléatoire, plus l'entropie est élevée. Les secrets utilisent l'entropie élevée pour permettre à différents services d'émettre indépendamment des secrets tels que des clés API sans craindre de créer un conflit potentiel (deux clés identiques).

Par ailleurs, sachez qu'un indice permet de réellement mesurer l'entropie d'une chaîne de caractères.

2. Utilisation d'une expression régulière pour détecter les secrets

Outre la recherche de chaînes à forte entropie, l'autre méthode courante consiste à trouver les clés qui suivent un modèle définissable et distinctif.

Par exemple, les clés Stripe, qui sont très sensibles, sont des clés préfixées. Elles commencent en effet toutes par les mêmes caractères "sk_live_". En utilisant une expression régulière (regex), nous pouvons détecter facilement ce type de clé.

exemple de clé Stripe

Cette clé est un exemple de clé bien sûr :-)

Comparaison des méthodes

Comparaison des méthodes de détection de secrets © GitGuardian

Comme on pouvait s'y attendre, il n’y a pas une méthode meilleure que l’autre ; en général, une détection fiable des secrets doit utiliser les deux méthodes en fonction des différents scénarios et des différents secrets. L'expression régulière ne sera possible qu'avec un nombre limité de types de secrets ; une entropie élevée devrait être utilisée pour capturer un éventail beaucoup plus large de secrets. 

Etape 2 : Filtrer les mauvais candidats

Trouver des secrets potentiels n'est qu'une partie de la solution. Il faut maintenant être capable de filtrer les faux positifs et de laisser les vrais positifs. C'est un véritable défi, car il faut maintenant agréger les différents signaux faibles autour de d’un secret potentiel donné pour déterminer s'il s'agit en fait d'un vrai positif ou d'un faux positif. Par exemple, il pourrait s'agir d'une clé de substitution, d'une chaîne à haute entropie utilisée comme numéro d'identification unique, d'une clé publique ou même d'une URL, ce qui est pratiquement impossible à déterminer si l'on ne regarde que la chaîne elle-même.

Il existe trois méthodes pour exclure les potentiels faux positifs :

  • Valider le candidat en effectuant un appel d'API
  • Filtrer les candidats qui contiennent des modèles éliminatoires.
  • Rechercher des motifs sensibles connus à l'intérieur du code (signaux faibles)

Encore une fois, comme pour les méthodes de détection, chacune d'entre elles présente des avantages et des inconvénients et doit être utilisée de concert lorsqu'on essaie de filtrer des secrets spécifiques.

Méthodes de filtrage des candidats © GitGuardian

Etude de cas : détection de secret appliquée

Maintenant que nous avons établi des méthodes communes de détection et de filtrage des secrets potentiels, nous pouvons examiner exactement comment GitGuardian met en œuvre toutes ces méthodes, ainsi que quelques éléments additionnels sur l'algorithme.

Vous ne serez probablement pas surpris d'apprendre que GitGuardian utilise toutes les méthodes décrites ci-dessus. Mais le plus intéressant est la façon dont chacune de ces méthodes est mise en œuvre.

Détecteurs monolithiques ou spécifiques

Une différence essentielle dans les capacités de détection de GitGuardian est le concept de construction de détecteurs spécifiques. Cela nous permet de sélectionner la méthode de détection et de filtrage la plus efficace, pour chaque secret spécifique.

Construire un seul algorithme monolithique pour détecter tous les secrets potentiels et les filtrer par lots rend difficile tout ajustement. Avoir une précision et un rappel plus élevés pour un type de secret peut signifier une performance moindre pour d'autres détecteurs.

On peut comparer cela au fait d'essayer d'utiliser un cargo pour pêcher. Vous faites des ajustements à cet énorme monolithe pour essayer de cibler un type spécifique de poisson, ce qui demande une énorme quantité de ressources. Le temps que les ajustements soient faits, le navire est maintenant hors trajectoire pour tous les autres poissons que vous essayez de cibler. Vous finissez par faire des ajustements constants, à grands frais de ressources et ne trouvez jamais le positionnement idéal.

En comparant cela à la méthode adoptée par GitGuardian, c'est comme avoir des centaines de petits bateaux individuels, chacun pour un type de poisson spécifique, vous pouvez faire autant de modifications que vous voulez sans avoir d'effet sur les autres bateaux.

L’important dans cette approche, c'est que ce n'est pas seulement la méthode de détection qui est spécifique à chaque secret. C'est aussi la méthode de filtrage.

Le fait de disposer de détecteurs individuels signifie qu'aucun compromis ne doit être fait lors du choix de la méthode de détection et de filtrage. Cela signifie également qu'il est possible d'adopter une approche par couches, dans laquelle les secrets découverts au moyen d'expressions régulières distinctives ont un poids plus important que les secrets découverts au moyen d'une détection entropique plus générique.

Mais c'est bien dans le filtrage des secrets potentiels que la méthode de détection individuelle s'avère la plus avantageuse. Bien sûr, il existe des indicateurs universels indiquant qu'un secret n'est pas valide, par exemple s'il s'agit d'une URL. Mais il existe un grand nombre d'indicateurs spécifiques aux secrets individuels qui influencent la probabilité d'un vrai positif. 

Par exemple, les modèles sensibles autour du contexte du secret peuvent différer grandement entre les types de secrets, ou différentes bibliothèques de modèles éliminatoires peuvent avoir des influences différentes sur les résultats. Différentes dépendances telles que les wrappers API peuvent modifier le taux de vrais positifs. En examinant chaque secret avec un regard spécifique et indépendant, nous sommes en mesure d'affiner nos résultats à un niveau impossible à atteindre avec un détecteur universel.

Le défi de cette stratégie est bien sûr d'agréger tous ces signaux faibles pour chaque secret indépendant, et pour être en mesure de découvrir ces indicateurs souvent très subtils, nous devons analyser d'énormes quantités de données.

Quand la donnée devient un avantage

La détection des secrets est, après tout, probabiliste. Pouvoir distinguer un vrai positif d'un faux positif lors de l'évaluation de la détection de secrets n'est pas un simple résultat binaire oui ou non. Elle repose sur des centaines de propriétés d'influence qui sont évaluées pour déterminer la probabilité d'un résultat vrai positif.

Quiconque a travaillé sur des algorithmes probabilistes ou de classification sait qu'il est essentiel de disposer d'une grande quantité de données pour alimenter l'algorithme. En outre, les secrets sont en constante évolution. Pensez à l'écosystème des cinq dernières années : combien de nouveaux services utilisez-vous actuellement ? Les services externes ont-ils modifié les propriétés de leurs secrets ? De nouveaux paquets sont-ils disponibles pour différents services ? Je suis sûr que vous comprendrez : les algorithmes de détection des secrets doivent changer, et être constamment améliorés et mis à jour. 

GitGuardian a commencé à scanner tous les commits publics de GitHub. Un milliard de commits par an pendant plus de 3 ans. L’accès à ce volume de donnée est un avantage certain pour améliorer le modèle.

Chaque fois qu’un secret potentiel est découvert, le système alerte le développeur et recueille ensuite des commentaires. Certains retours sont explicites, comme le fait de marquer l'alerte comme vrai ou faux positif, mais certains retours implicites sont aussi pris en compte, comme le fait que le dépôt ait été supprimé après l'alerte. 

Toutes ces informations sont ensuite réinjectées dans l'algorithme, ce qui donne des résultats attendus et d'autres totalement inattendus sur certains des signaux faibles qui ont influencé le taux de vrais positifs. Sans analyser autant de données, il serait très difficile de rassembler et de noter tous les facteurs d'influence de la détection des secrets.

Points essentiels à retenir

Les méthodes les plus courantes pour détecter les secrets sont l'identification de chaînes à haute entropie et l'utilisation d'expressions régulières pour trouver des modèles définissables. Seules quelques clés peuvent être identifiées à l'aide d'expressions régulières car elles nécessitent l'existence de motifs cohérents pour chaque secret, les autres secrets peuvent être identifiés en détectant des chaînes à forte entropie. Aucune de ces méthodes ne produit à elle seule des résultats totalement exacts ; les candidats doivent donc être filtrés. Les méthodes courantes de filtrage des secrets comprennent : l'utilisation de dictionnaires de motifs éliminatoires, l'analyse du contexte d'un secret présumé et la validation des informations d'identification par un appel d'API.

C'est dans le filtrage des candidats que réside une grande partie du problème de la détection des secrets. En utilisant GitGuardian comme étude de cas, nous pouvons voir que les meilleurs résultats peuvent être obtenus en créant des détecteurs individuels et en agrégeant des signaux spécifiques, souvent faibles, pour détecter et filtrer efficacement les secrets potentiels. Ces caractéristiques et signaux faibles peuvent être difficiles à détecter sans analyser une énorme quantité de données.