DEV Community

NotFunnyThomas for Onepoint

Posted on

Et BetterLeaks n'aura plus de secrets pour vous..

J'avais envie de vous parler d'autre chose que d'IA pour cet article.

Donc petite découverte de l'été, pas forcément très frais (comme cet été) mais on fera avec.

A l'heure des grands discours sur la sécurité, je me suis demandé si mes repos étaient irréprochables en tant que dev. En terme de clés d'API, tokens, mots de passe, certificats privés.
Est ce qu'un secret avait déjà été commité ? Celui-ci a-t-il été ensuite masqué par un second commit ?
Est ce que l'équipe aurait cherché à cacher des boulettes ? (non je n'ai pas faim à l'heure où j'écris ces lignes)

Un outil remplit assez bien le but que je m'étais fixé et que j'ai découvert :

Betterleaks

Qu'est ce que c'est?

Un scanner de secrets open-source écrit en Go, créé pour la petite histoire par Zach Rice, l'auteur original de Gitleaks.
Positionné comme un remplaçant de Gitleaks, plus rapide, facilement configurable et pensé pour les workflows modernes (CI/CD, agents IA). Autant de points que je ne vérifierai pas dans cet article, je vais me concentrer sur Betterleaks et son fonctionnement premier.

L'intégration peut se faire à plusieurs niveaux :

  • en pre-commit hook local
  • dans un pipeline CI (GitLab CI, GitHub Actions)
  • lors d'audits ponctuels sur des repos ou buckets

J'ai choisi pour cette expérimentation de le faire en mode audit ponctuel pour me permettre d'appréhender un peu mieux cet outil.


💡 ALERT INFO

Petite précision pour la suite; j'ai trouvé Betterleaks assez verbeux de base, et je me suis dit que j'allais rendre ça un peu plus sexy avec un joli dashboard réalisé avec TamboUI (pour construire des interfaces terminal en Java) pour me permettre de naviguer dans mes détections de fuites.
Pour plus d'infos, allez voir l'excellent article de mon collègue sur Tamboui

Donc les captures d'écran que vous verrez par la suite sont issus de ce dashboard.


Voyons maintenant ce que Betterleaks propose en usage direct.
De base, vous pouvez exécuter un scan de votre système de fichier local où est contenu votre projet.

betterleaks dir . -v
Enter fullscreen mode Exit fullscreen mode

L'option -v n'est là que pour activer le mode verbose et avoir plus de détails sur chaque résultat, en plus des stats de scan.

Pour ma part, j'avais 6 secrets détectés, principalement des API keys.

Pour aller plus loin, il est également possible de scanner l'ensemble de votre repository pour détecter des secrets éventuels dans l'ensemble de l'historique Git du projet.

betterleaks git /path/to/myrepo -v 
Enter fullscreen mode Exit fullscreen mode

ou bien de ne scanner que le staging.

betterleaks git . --pre-commit --staged
Enter fullscreen mode Exit fullscreen mode

Mais comment détecte-il les secrets et comment évite-t-il les faux positifs ?

Si l'on rentre un peu dans le détail, le fonctionnement du moteur de Betterleaks se décompose ainsi pour minimiser les faux positifs :

  1. Un Prefilter. Il est exécuté avant toute règles et n'a accès qu'aux métadonnées (chemin, auteur Git, message de commit…). Son but est de filtrer tous les fichiers/dossiers qui n'ont pas de rapport avec votre code source (node_modules, vendor, binaires, bots comme renovate, etc.).

  2. Une détection par règles. Chaque règle ([[rules]] dans le TOML) définit une regex, des keywords et une description. Le scan est parallélisé (via l'option --source-workers).

  3. Un filter (post-match) écrit en Expr. Expr est un mini-langage Go d'expressions booléennes évaluées à la volée, utilisé pour exprimer des règles de filtrage directement dans la config. Par exemple :

    // Rejette tout match dont l'entropie est basse ou qui vient d'un fichier Markdown.
    filter.entropy(finding["secret"]) < 3.0 || attributes["path"] endsWith ".md"
    

    Voir (Filter-Functions). Pour résumer, c'est une allowlist exprimée en Expr. L'Expr décrit ce qui n'est pas un secret. Tout ce qui matche est alors écarté du rapport. Il a accès aux métadonnées et aux résultats.

  4. Validate. C'est une étape optionnelle qui fait un appel HTTP asynchrone (ex: api.github.com/user) pour confirmer que le secret est bien actif et retourne si celui-ci est valid, invalid ou unknown.

Mais attardons nous sur la partie Filter et les fonctions disponibles. Deux fonctions sont particulièrement intéressantes et méritent d'être détaillées :

Fonction basée sur l'entropie

filter.entropy(string)
Enter fullscreen mode Exit fullscreen mode

Cette fonction se base sur l'entropie pour détecter un éventuel secret.

Pour rappel, l'entropie (au sens de Shannon) mesure le degré d'imprévisibilité ou de désordre d'une source d'information. Plus une source est imprévisible, plus son entropie est élevée.

  • Par exemple, une chaine de caractère "aaaaaaaa" aura une entropie nulle. On sait à l'avance ce qui va arriver.

  • Mais pour la chaine de caractère "xA9fP2qL0sRw", chaque caractère est quasi unique, sur un alphabet mélangeant minuscules, majuscules et chiffres (environ 60 possibilités). Son entropie est autour de 3.5 bits/caractère.

Cette mesure va servir pour déduire si une chaine de caractère est bien détectée en tant que secret ou non (si la valeur d'entropie dépasse un seuil prédéfini). Et ainsi réduire le nombre de faux positifs.

On peut d'ailleurs voir que pour chaque type de règle définie, un seuil différent est paramétré (config)

Fonction basée sur le token efficiency

filter.tokenRatio(string)
Enter fullscreen mode Exit fullscreen mode

Le Token Efficiency filtering utilise une tokenisation BPE (Byte Pair Encoding) pour évaluer la « rareté » d'une chaîne : une string qui ressemble à du langage naturel est écartée, un blob random typique d'un secret est conservé.

Pour rappel, la tokenisation BPE découpe un texte en morceaux ("tokens") issus d'un vocabulaire appris à l'avance : les paires de caractères les plus fréquentes du corpus d'entraînement sont fusionnées itérativement en tokens plus longs. Le langage courant se compresse en peu de tokens longs, alors qu'une chaîne inédite (typique d'un secret) se fragmente en beaucoup de tokens quasi octet par octet, ce qui donne un signal exploitable pour la détection.

La detection de vrais secrets est nettement améliorée avec cette combinaison Token Efficiency + Entropie par rapport à l'entropie classique seule. La proportion de vrais secrets effectivement détectés par rapport à ceux qui existent réellement passe de 70 % (entropie seule) à ~99 % en combinant les deux, selon les auteurs.

C'est vraiment là la force de Betterleaks.


Notons aussi que certains secrets vivent en paire : un client_id + un client_secret, une AWS access_key + une secret_key. Betterleaks sait également exiger la coprésence via une configuration spécifique dans la règle de détection.

Comment jouer avec les règles

Et si nous avons maintenant envie de rajouter une règle custom ?

Betterleaks utilise le format TOML pour ses fichiers de configuration. Il cherche par ordre de priorité :

  • Le fichier passé via --config <path>
  • Un fichier .betterleaks.toml à la racine du repo scanné
  • Sinon, le config par défaut embarqué dans le binaire

Attention, il ne charge pas automatiquement les règles internes par defaut. Le chargement des règles est exclusif si on ne lui précise pas le contraire :

[extend]
useDefault = true
Enter fullscreen mode Exit fullscreen mode

Par exemple pour des tokens internes de la forme alf<32 hex>_ que mon application utilise, nous pouvons rajouter :

# .betterleaks.toml à la racine du repo
title = "myapp custom secrets"

[extend]
useDefault = true

[[rules]]
id          = "myapp-internal-token32hex"
description = "Token interne myapp (format alf_<hex32>)"
regex       = '''\b(alf_[a-f0-9]{32})\b'''
keywords    = ["alf_"]
confidence  = "high"

filter = '''
filter.entropy(finding["secret"]) < 3.0
'''

Enter fullscreen mode Exit fullscreen mode

Nous pouvons ensuite itérer sur notre nouvelle règle et faire les refacto nécessaires pour l'ajuster grâce à Playground qui permet de tester sa nouvelle règle sans avoir besoin de lancer la CLI.

Pour aller plus loin

Il est possible de customiser ses rapports si vous n'utilisez pas un TUI (et surtout si vous l'industrialisez dans une pipeline de CI), un exemple de commande :

betterleaks git . \
  --report-format json \
  --report-path findings.json \
  --redact \
  --exit-code 1
Enter fullscreen mode Exit fullscreen mode

Nous avons plusieurs choix de format (json, csv, junit, etc.), la possibilité de masquer les secrets dans les rapports (l'option redact), le code retour pour faire échouer la CI, et d'autres options sur lesquelles je ne m'attarderai pas.


Le fichier .betterleaksignore permet de supprimer des résultats qui seraient détectées comme faux positif par vous-même.

En conclusion

Betterleaks est plutôt facile à prendre en main et à customiser. Il est pensé pour s'intégrer facilement à tous les niveaux du cycle de vie de votre projet.

J'espère que je vous ai donné envie de; a minima l'utiliser sur votre repository par curiosité, ou a fortiori, de l'intégrer à votre pipeline CI/CD ou en pre-commit hook !

Pour les interessés, le TUI réalisé est ici

Ref : https://github.com/betterleaks/betterleaks

Top comments (0)