Note: Zach Rice, Head of Secrets Scanning at Aikido, is also the founder of Gitleaks. This post originally appeared on his blog, where he covers secrets scanning, software engineering, and open source topics.
Dans « Regex is (almost) All You Need », nous avons appris que l'utilisation combinée de modèles d'expressions régulières, d'entropie et de filtres basés sur des règles constitue un moyen efficace de détecter les secrets potentiels. Regex est utilisé pour lancer un large filet afin d'identifier les candidats. L'entropie est utilisée comme filtre principal sur les candidats capturés, puis des filtres supplémentaires, tels que la présence de mots anglais couramment utilisés ou le filtrage de fichiers « sûrs » connus comme go.sum, sont appliqués en dernier.
Entropy fait un travail correct pour filtrer les faux positifs, mais laisse beaucoup à désirer, en particulier lors de l'évaluation des secrets génériques. Existe-t-il une meilleure solution qu'Entropy pour ce filtre primaire post-capture regex ? Cet article examine si le codage par paires d'octets (Byte-Pair Encoding) peut constituer une alternative plus efficace à Entropy pour analyse des secrets.

Entropie
Mais qu'est-ce que l'entropie ? Selon John von Neumann, lors d'une conversation avec Claude Shannon, «personne ne sait vraiment», mais Wikipédia le fait. L'entropie de Shannon mesure l'imprévisibilité moyenne d'une chaîne, c'est-à-dire la quantité d'informations que chaque caractère véhicule. Lorsque les caractères sont répartis de manière uniforme (nombreux caractères distincts, absence de schéma clair), chacun d'entre eux est plus difficile à prédire, l'entropie est donc élevée. Lorsque quelques caractères dominent, le caractère suivant est facile à deviner, l'entropie est donc faible. En pratique, cela signifie quelque chose comme aaaaaa111111 obtient un score faible, tandis que quelque chose comme xA9fP2qL0sRw obtient un score élevé. En ce qui concerne détection de secrets, cela fait de l'entropie un premier pas décent pour repérer les chaînes « d'apparence aléatoire » (secrets candidats).
Mais voulons-nous vraiment que le hasard soit notre principal filtre pour détection de secrets? Excusez-moi d'utiliser ici le trope LLM « ce n'est pas X, c'est Y », mais les secrets ne sont pas seulement aléatoires, ils sont statistiquement inhabituels par rapport à la distribution naturelle des textes écrits par des humains. En termes plus simples, les secrets sont rares. Une chaîne codée en b64, un UUID, un secret réel et une chaîne de dépendance à l'apparence étrange peuvent avoir des scores d'entropie similaires, même s'ils sont fondamentalement différents dans leur fréquence d'apparition dans le monde réel. L'entropie ne permet pas de faire la différence entre « cela semble aléatoire » et « cela n'apparaît presque jamais dans un texte ou un code source en anglais ». Au lieu de mesurer le caractère aléatoire à l'aide de l'entropie, pourquoi ne pas essayer de mesurer à quel point une chaîne est hors vocabulaire ou non naturelle?
Byte-pair encoding
Bon, alors comment détecter à quel point une chaîne de caractères semble peu naturelle ou rare ? Grâce au codage par paires d'octets (BPE), bien sûr ! La tokenisation par codage par paires d'octets reflète implicitement la distribution de fréquence du texte sur lequel elle a été entraînée. Les mots et sous-mots courants sont fusionnés en tokens longs, tandis que les chaînes rares ou peu naturelles sont divisées en plusieurs tokens courts.
Voici quelques exemples utilisant letokenizer1 cl100k_base :
- « Bonjour tout le monde » → [15339, 1917]
- « regarder l'ordinateur » → [20986, 266, 44211]
- « kj2h3f2fuaafewa » → [93797, 17, 71, 18, 69, 17, 69, 4381, 2642, 365, 64]
Comme BPE construit son vocabulaire en fusionnant de manière répétée les paires de caractères les plus courantes dans les données d'entraînement, sa tokenisation reflète naturellement la fréquence d'apparition des différents modèles. Cela ressemble un peu à cette notion de rareté que nous essayons de mesurer, n'est-ce pas ?
Les mots anglais courants ont leur propre jeton individuel car ils apparaissent fréquemment dans la formation, par exemple « password » est le jeton [3918]. « github » est le jeton [5316]. « function » est le jeton [1723]. Mais qu'en est-il d'une clé API aléatoire telle que « ghp_xK7mP9qL2wR5nT3vJ8fY » ?

Le tokenizer n'a probablement jamais vu cette séquence spécifique pendant l'entraînement, il divise donc la chaîne en paires plus petites, pour finalement revenir à des octets individuels qui finissent par être tokenisés en [876, 79, 3292, 42, 22, 76, 47, 24, 80, 43, 17, 86, 49, 20, 77, 51, 18, 85, 41, 23, 69, 56]. Cela fait 22 tokens pour une chaîne de 24 caractères, ce qui signifie que le tokenizer n'a pratiquement rien reconnu.
Consultez https://tiktokenizer.vercel.app/?model=cl100k_base pour voir comment différentes chaînes sont tokenisées.
Token efficiency
Si les tokeniseurs BPE divisent les chaînes rares en plusieurs tokens courts, nous pouvons alors mesurer la rareté d'une chaîne en comparant la longueur de la chaîne d'origine au nombre de tokens produits. Appelons cela Efficacité des jetons.efficacité_des_jetons = longueur(chaîne) / longueur(jetons)
Le langage naturel correspond bien au vocabulaire du tokenizer, donc les expressions courantes produisent moins de tokens. Les chaînes de caractères ressemblant à des secrets n'y correspondent pas, elles produisent donc beaucoup de tokens.
Prenons notre exemple de ghp_xK7mP9qL2wR5nT3vJ8fY. Son efficacité en termes de jetons est de 1,1 (une chaîne de 24 caractères produisant 22 jetons). Une phrase telle que Bonjour tout le monde a une efficacité de 3,7 (11 caractères divisés en 3 jetons). Si les secrets produisent systématiquement des scores d'efficacité des jetons plus faibles et que les textes courants produisent des scores plus élevés, alors l'efficacité des jetons pourrait être un filtre post-regex utile pour détection de secrets.
Pour tester cette hypothèse, nous pouvons nous tourner vers l'ensemble de données CredData, qui contient des milliers d'exemples étiquetés de secrets véritables et de non-secrets extraits de référentiels réels. Si l'efficacité des tokens reflète réellement la rareté ou le caractère « non courant » du langage, alors l'examen de la distribution de l'efficacité des tokens sur les valeurs secrètes de l'ensemble de données CredData pourrait révéler un écart entre les secrets et les non-secrets.
CredData
Le jeu de données CredData est divisé en fichiers d'index et fichiers de données. Les fichiers d'index stockent les métadonnées dont vous avez besoin, telles que les étiquettes, les plages de lignes et de colonnes, et les noms de fichiers. Ils ne contiennent pas les valeurs secrètes réelles, vous devez donc reconstruire chaque secret en découpant les fichiers source aux plages spécifiées. C'est l'approche que j'ai adoptée. J'ai extrait chaque valeur secrète étiquetée directement à partir de l'ensemble de données. Cela signifie que nous n'évaluons pas si l'efficacité des jetons permet à elle seule de détecter les secrets. Nous évaluons plutôt si l'efficacité des jetons permet de classer les secrets candidats déjà capturés, ce qui en fait une étape de filtrage post-regex plutôt qu'un détecteur autonome.
Vous pouvez consulter le code qui génère ces graphiques ici:

Cela semble prometteur ! Il semble que 2,5 soit un bon seuil minimum pour l'efficacité des jetons. Gitleaks utilise un seuil d'entropie de 3,5 pour les secrets génériques.
À l'aide de ces seuils, examinons les classifications.

Efficacité du jeton : Précision =57,3% Rappel =98,6% F1=0,725
Entropie: Précision=21,1% Rappel=70,4% F1=0,325Un taux de rappel de 98,6 % est plutôt excellent. Nous classons correctement presque tous les véritables secrets, ne laissant que 149 faux négatifs. Il y a un nombre raisonnable de faux positifs pour l'efficacité des jetons, mais la différence entre celle-ci et l'entropie est énorme. L'entropie génère 28 000 faux positifs (près de 4 fois plus que l'efficacité des jetons) et 3 000 faux négatifs. L'ajout d'un simple filtre de mots améliore les deux méthodes, mais l'efficacité des jetons reste meilleure en termes de score F1. Le filtre de mots ignore les secrets contenant plus d'une occurrence d'un mot de 4 caractères ou plus.

TE + Filtre de mots : Précision =80,4% Rappel =95,8% F1=0,874
Entropie + filtre de mots : Précision =76,6% Rappel =67,1% F1 =0,715Ce filtre effectue une grande partie du travail fastidieux lié à l'entropie, mais il nous aide également à filtrer les FP pour améliorer l'efficacité des jetons. En termes d'efficacité des jetons, nous sommes passés de 7894 FP à 2508 FP, tout en n'introduisant que 308 nouveaux FN lors de l'application de ce filtre de mots, ce qui nous aide considérablement à améliorer le score F1.
Si vous souhaitez essayer de reproduire ces résultats, vous pouvez consulter une partie du code sur mon Github.
Exemples
Examinons quelques secrets que l'entropie néglige, mais que l'efficacité des jetons permet de détecter.
e2aa9ae57d893a1
Ce type a une entropie de 3,125. C'est assez élevé, mais pas tout à fait 3,5, qui est la valeur seuil utilisée par Gitleaks et certains autres détecteurs de secrets. e2aa9ae57d893a1 produit [68, 17, 5418, 24, 6043, 3226, 67, 26088, 64, 717] pour ses jetons cl100k_base, ce qui donne une efficacité de jeton de 1,6, bien en dessous du seuil d'efficacité de jeton de 2,5.
mcjrx4
Nous avons ici un mot de passe, qui n'est d'ailleurs pas très bon. Les mots de passe constituent une catégorie difficile pour le filtre d'entropie, car ils sont souvent (et malheureusement) courts, et les chaînes courtes ont généralement des valeurs d'entropie faibles. Celui-ci a une entropie de seulement 2,58. Mais le tokeniseur le décompose en tokens proches du niveau octet [13183, 73, 12940, 19], ce qui lui confère une efficacité de token de 1,5. Six caractères, quatre tokens. Le tokeniseur ne le reconnaît pas comme du langage naturel, et c'est exactement le signal que nous recherchons.
U@kkf8fo !!
Un autre mot de passe. Celui-ci est intéressant en raison des caractères spéciaux qu'il contient. L'un des défis de détection de secrets pour les secrets et mots de passe génériques, consiste à créer une expression régulière qui capture la plupart secrets. Le problème avec l'utilisation d'une expression régulière qui vise à capturer la plupart Le problème avec les secrets, c'est qu'ils peuvent laisser passer beaucoup de faux positifs, comme les e-mails, les URL, etc. Donc, pour chaque caractère spécial comme @ ou ! ou / vous définissez dans la classe de caractères de votre groupe de capture, vous augmentez les risques d'introduire davantage de faux positifs. C'est pourquoi le groupe de capture générique de Gitleaks est assez strict : [\w.=-]{10,150}Avec un filtre d'efficacité des jetons, nous pourrions potentiellement assouplir ce modèle afin d'inclure davantage de caractères spéciaux. Dans ce contexte, voici comment l'entropie et l'efficacité des jetons se comparent dans cet exemple. U@kkf8fo !! a une entropie de 2,72 et produit les tokens suivants [52, 31, 19747, 69, 23, 831, 51447] avec une efficacité de token de 1,42 (10 caractères, 7 tokens).
Une petite remarque concernant les mots de passe. L'efficacité des jetons ne permet pas de bien classer les mauvais mots de passe tels que « password123 » ou « chibearsfan123 ». Ces mots de passe sont essentiellement du langage naturel, ce qui signifie qu'ils ont une valeur d'efficacité des jetons élevée. Les phrases de passe ne sont pas non plus très efficaces, car il s'agit généralement de mots simples.
Performance
L'impact sur les performances est négligeable. Le temps moyen par chaîne pour calculer l'entropie des secrets CredData capturés est de 4,55 µs, contre 11,75 µs pour calculerl'efficacité des jetons2 (à l'aide de cl100k_base). Une différence de 2,5 fois peut sembler importante, mais il faut garder à l'esprit que lorsqu'il s'agit de détection de secrets goulot d'étranglement réside dans les expressions régulières, et non dans les filtres rapides tels que l'entropie ou l'efficacité des jetons qui viennent après.
Token efficiency w/ Betterleaks
Les responsables de la maintenance de l'ensemble de données CredData ont créé un impressionnant scanner de secrets appelé CredSweeper qui utilise des expressions régulières, l'entropie et des réseaux neuronaux récurrents (RNN) pour détecter les secrets. Dans un monde où « les LLM peuvent détecter les secrets sans AUCUN faux positif » (tant dans le milieu universitaire que dansl'industrie3), il est rafraîchissant de voir les ingénieurs de Samsung mettre au point un détecteur de secrets basé sur un « apprentissage automatique plus traditionnel ». Bravo ! CredSweeper affiche un score F1 impressionnant de 0,85 lorsqu'il est testé avec CredData. C'est plutôt bon ! Voyons si nous pouvons le battre avec le nouveau filtre Token Efficiency dans Betterleaks.
Ah oui, c'est vrai. Qu'est-ce que Betterleaks ? C'est un nouveau projet qui s'appuie sur l'héritage de Gitleaks. J'en parlerai plus en détail dans un autre article, mais tout ce que vous devez savoir, c'est qu'il s'agit d'un remplacement direct de Gitleaks dont je m'occupe... et qu'il sera meilleur... grâce à son nom.
Cette configuration ajoute quelques nouvelles règles et modifie légèrement certains éléments de la configuration par défaut existante. L'utilisation de cette configuration et l'exécution de Betterleaks sur l'ensemble de données CredData donnent un score F1 de 0,892.
(Efficacité des jetons + (faible) entropie sur la règle générique + ajustements de la règle) Résultats du benchmark :
========================================
TP (vrais positifs) : 10796
FP (faux positifs) : 1031
TN (vrais négatifs) : 42572
FN (faux négatifs) : 1578
----------------------------------------
Précision : 0,9534
Précision: 0,9128
Rappel: 0,8725
Score F1 : 0,8922Très bien.

En utilisant uniquement l'efficacité des jetons, nous obtenons :
(Juste efficacité des jetons + ajustements des règles) Résultats du benchmark :
========================================
TP (vrais positifs) : 10843
FP (faux positifs) : 1722
TN (vrais négatifs) : 41881
FN (faux négatifs) : 1531
----------------------------------------
Précision : 0,9419
Précision: 0,8630
Rappel: 0,8763
Score F1 : 0,8696Sans utiliser de seuil d'entropie faible sur la règle générique lors de l'utilisation du filtre d'efficacité des jetons, nous introduisons environ 700 faux positifs. Néanmoins, sans ce filtre d'entropie sur la règle générique, nous obtenons un F1 de 0,86, ce qui n'est pas mauvais.
Comment pouvons-nous attribuer des points sans le filtre d'efficacité des jetons, mais en nous appuyant uniquement sur des ajustements des règles et l'entropie ?
(Juste entropie + ajustements des règles) Résultats du benchmark :
========================================
TP (vrais positifs) : 8498
FP (faux positifs) : 1041
TN (vrais négatifs) : 42562
FN (faux négatifs) : 3876
----------------------------------------
Précision : 0,9122
Précision: 0,8909
Rappel: 0,6868
Score F1 : 0,7756Bon, alors 0,892 contre 0,776, c'est une différence assez importante. L'utilisation du seul filtre d'entropie ajoute plus de 2 000 FN et 80 FP par rapport au filtre d'efficacité des jetons.
Vous pouvez consulter le code du filtre Token Efficiency sur le Github de Betterleaks.
func (d *Detector) failsTokenEfficiencyFilter(secret string) bool {
analyzed := secret
if len(analyzed) < 20 && strings.ContainsAny(analyzed, "\n\r") {
analyzed = newlineReplacer.Replace(analyzed)
}
tokens := d.tokenizer.Encode(analyzed, nil, nil)
matches := words.HasMatchInList(analyzed, 5)
if len(matches) > 0 {
return true
}
threshold := 2.5
if len(analyzed) < 12 {
threshold = 2.1
matches := words.HasMatchInList(analyzed, 3)
if len(matches) == 0 {
threshold = 2.5
}
}
return float64(len(analyzed))/float64(len(tokens)) >= threshold
}Le filtre est légèrement adapté par rapport à celui utilisé dans la comparaison des graphiques. Cela permet de prendre en compte les mots de passe courts et les secrets contenant des sauts de ligne (nous supprimons les sauts de ligne avant d'effectuer l'analyse de l'efficacité des jetons sur le candidat).
Quelques remarques supplémentaires :
- Je n'ai pas réussi à faire fonctionner le script de benchmarking fourni par CredData, alors j 'ai(claude) créé le mien. Désolé si ce n'est pas très scientifique, mais vous pouvez vérifier mon travail (claude) puisqu'il est open source.
- Les doublons ont été supprimés de l'ensemble de données CredData.
- Toutes les nouvelles règles et modifications apportées aux règles existantes n'étaient pas « secrètes ». Autrement dit, j'ai essayé de ne pas manipuler le benchmark.
- Certains secrets étiquetés comme FP dans l'ensemble de données CredData semblent avoir été étiquetés de manière erronée, donc honnêtement, le couple F1 pourrait être de +/ 0,05 peut-être.
1 Pour tous les exemples que nous examinerons, nous utiliserons le tokenizer cl100k_base.
2 Temps mesuré à partir du script générateur de graphiques qui calcule l'entropie et l'efficacité des jetons pour chaque secret candidat.
Remerciements: Je tiens à remercier l'utilisateur GitHub« DmitriyAlergant »pour avoir soumis cette idée dans un ticket sur le dépôt Gitleaks. Un grand merci aux responsables de CredData/CredSweeper pour m'avoir aidé à me lancer dans cette aventure.

