
Triage des expositions d'identifiants et de données sensibles pour les partages de fichiers
Tri des expositions d'identifiants et de données sensibles dans les partages de fichiers.
Lorsqu'un partage ouvert apparaît, la question n'est jamais « est-ce que ce dépôt contient une clé divulguée ? ». C'est « qu'est-ce qui vient d'être exposé, et que dois-je révoquer avant la fermeture des bureaux ? ». sift est conçu pour cette question : rappel élevé, file d'examen rapide, et une boucle de rétroaction pour que tout ce que vous repérez à l'œil nu devienne une règle qui trouve les deux cents autres copies.

Python 3.11+, bibliothèque standard uniquement. Pas de pip install, pas d'internet, aucune étape de compilation. Il fonctionne sur un ordinateur portable d'IR durci, là où vous en avez besoin.```bash sift survey \fileserver\openshare # how big is this thing sift copy \fileserver\openshare C:\IR\case-4471 # take a throttled copy sift scan C:\IR\case-4471 # scan it, opens the triage UI
L'analyse sur place fonctionne aussi. Essayez-la d'abord sur un partage d'identifiants factices :```bash
sift demo C:\temp\demoshare
sift.cmd est un lanceur qui fonctionne depuis n'importe quel répertoire. Pour taper sift au lieu du chemin complet, ajoutez C:\Dev\sift à PATH :```bash
setx PATH "%PATH%;C:\Dev\sift"
Pour une machine sans Python du tout, `python build_portable.py` construit
`dist/sift-secrets-<version>-portable-win64.zip` : le runtime embarquable officiel de python.org
plus cette arborescence source, à décompresser et exécuter via le
`sift.cmd` fourni. ~11 Mo, pas d'installation, pas de droits administrateur, et rien n'y est
compilé ni réemballé — voir la docstring dans `build_portable.py` pour comprendre pourquoi
c'est mieux qu'un .exe figé sur un ordinateur portable IR verrouillé.
---
## Pourquoi pas simplement gitleaks ou trufflehog
Ce sont tous deux de bons outils qui résolvent un problème différent.
Ce sont des outils de **précision** conçus pour la CI, où un faux positif coûte à un
développeur son après-midi, alors ils ne se déclenchent principalement que sur des éléments ressemblant à une
clé API de fournisseur connue. trufflehog va plus loin et privilégie les secrets qu'il peut *vérifier* en
appelant l'API du fournisseur, ce qui est un signal vraiment excellent que la regex
ne peut pas reproduire.
Le tri des partages inverse l'économie. Un humain lit déjà chaque résultat, donc un
faux positif coûte trois secondes. Ce qui vous coûte, c'est un **faux négatif**.
Les clés API de fournisseurs fuient bel et bien sur les partages - une sauvegarde de la racine web, un script
de déploiement, le dossier de projet de quelqu'un copié sur le lecteur du département, et il y a
un `.env` avec une clé Stripe active dedans. Elles valent la peine d'être détectées, et sift
les détecte. Mais c'est aussi la partie que gitleaks et trufflehog gèrent déjà
bien. La lacune, c'est tout le reste, et sur un partage de fichiers c'en est la majeure partie :
| Ce que les scanners CI manquent | Pourquoi ils passent à côté |
|---|---|
| `web.config` avec une chaîne de connexion SQL | Pas un format de clé connu, aucun fournisseur pour vérifier |
| `Map-Drives.ps1` avec `net use ... /user:` | Juste une commande shell avec un mot après |
| `New Hire Setup Guide.docx` | Fichier Office, lu comme binaire, entièrement ignoré |
| `unattend.xml`, GPP `Groups.xml` | Artefacts de déploiement Windows pour lesquels personne n'a écrit de détecteur |
| `confCons.xml`, `.rdg`, WinSCP.ini | Mots de passe stockés de manière réversible, mais pas un « format secret » |
| `passwords.xlsx` | C'est un ZIP. Les scanners de texte brut voient du binaire et passent à autre chose |
| `.kdbx`, `.pfx`, `id_rsa` | Octets opaques - le *nom de fichier* est la trouvaille |
| Un `.bak` avec une chaîne de connexion à l'intérieur | Binaire, donc jamais lu |
sift couvre tout cela, fournit ses propres règles de clés de fournisseur, et **importe les packs de règles
et les résultats d'autres outils** - gitleaks TOML, Kingfisher/Titus YAML, et
trufflehog JSON - donc vous n'avez pas à choisir entre les outils.
L'art antérieur le plus proche est [Snaffler](https://github.com/SnaffCon/Snaffler), qui est
excellent dans la moitié nom-de-fichier-et-classification de ce problème et est l'inspiration
directe pour les règles de noms de fichiers. Ce qu'il n'a pas - et ce qui s'avère
être le véritable goulot d'étranglement une fois que vous avez 400 résultats - c'est une boucle de revue.
---
## La boucle
1. **Scannez** le partage.
2. **Traitez la file d'attente.** Chaque résultat montre les lignes environnantes avec la correspondance
surlignée. Les touches fléchées élargissent le contexte ; un clic ouvre le fichier entier dans
VS Code à cette ligne, ou dans Notepad.
3. **Repérez un oubli.** Vous en trouverez. Surlignez-le dans l'aperçu et appuyez sur `r`.
4. **sift propose des motifs** et vous indique en direct combien de fois chacun correspondrait
sur tout ce qui a déjà été lu.
5. **Enregistrez-le.** La renumérisation en cache prend environ une seconde, et les nouveaux résultats apparaissent
dans la file d'attente sans que vos décisions de tri existantes soient modifiées.
L'étape 5 est ce qui rend le reste digne d'intérêt. Les résultats sont indexés sur
`(path, rule, line, value-hash)`, donc une renumérisation réinsère les mêmes lignes et vos
statut, notes et propriétaire suivent. Sans cela, vous réexamineriez les mêmes
300 résultats à chaque itération et abandonneriez à la troisième.
---
## Commandes```bash
# size it up first: file count, total bytes, biggest folders, transfer estimates
sift survey \\fileserver\share
# take a rate-limited local copy (resumable; gentle = 5 MB/s by default)
sift copy \\fileserver\share C:\IR\case-4471 --speed gentle
# scan a share and open the triage UI
sift scan \\fileserver\share
# maximum recall: more noise, but a human is reading anyway
sift scan D:\dfs\dept --tier 3
# re-open the UI over the most recent scan
sift ui
# build a share of fabricated credentials, scan it, open the UI
sift demo C:\temp\demoshare
# inherit other tools' vendor-key rules, then use them in the live rescan loop
sift import-rules gitleaks.toml # gitleaks TOML
sift import-rules path/to/kingfisher/data/rules # a directory of YAML
# pull in what the other scanners found, into the same queue
trufflehog filesystem \\fileserver\share --json > th.json
sift import-findings th.json
# hand off to the incident record (redacted unless you say otherwise)
sift export --fmt pdf --status confirmed --out ir-4471.pdf
sift export --fmt csv --out ir-4471.csv
sift export --fmt pdf --no-redact # plaintext; handle as evidence
sift rules # what is loaded
sift selftest # detection tests against a synthetic share
# ask vendors whether confirmed findings are still live. NETWORK. Opt-in.
sift validate --status confirmed
Partout où sift apparaît, vous pouvez utiliser python -m sift à la place, depuis le
répertoire C:\Dev\sift.
Les résultats vont dans un dossier par cible sous %LOCALAPPDATA%\sift\, jamais dans le
répertoire de travail - la base contient des identifiants en clair, et exécuter l'outil depuis
votre répertoire personnel ne devrait pas en déposer silencieusement un là. Chaque partage
a son propre store, donc deux missions ne partagent jamais de file de tri. sift ui
sans argument rouvre le plus récent ; --data DIR le remplace.
Les règles personnalisées sont globales, dans %LOCALAPPDATA%\sift\user-rules.json, donc un
motif que vous écrivez lors d'une mission vous aide sur le prochain partage que vous examinez.
Les deux se trouvent dans l'en-tête de l'UI, à côté du champ de chemin, et dans la CLI.
Vérifier la taille est un parcours qui n'effectue que des stat. Rien n'est ouvert, c'est donc peu coûteux même sur SMB, et cela indique le nombre de fichiers, le total d'octets, les plus grands dossiers, la répartition par extension, quelle quantité sift lirait réellement, et combien de temps une copie prendrait à chaque vitesse. Pointer un scanner vers une racine DFS inconnue puis attendre, voilà comment un après-midi disparaît.
Copier localement tire d'abord le partage vers un dossier local. Cela vaut le coup parce que :
Le transfert est limité en débit, 5 Mo/s par défaut. Saturer la liaison vers un serveur de fichiers de production à 14 h transforme votre investigation en un second incident. Débridez-le lorsque vous savez que le chemin est inactif.
| Vitesse | Débit |
|---|---|
| douce (défaut) | 5 Mo/s |
| normale | 25 Mo/s |
| rapide | 100 Mo/s |
| illimitée | tout ce que la liaison permet |
Les transferts reprennent : un fichier de destination avec la même taille et la même date de modification est ignoré, donc une acquisition interrompue à 80 % reprend là où elle s'est arrêtée. Les fichiers verrouillés ou refusés sont enregistrés et ignorés plutôt que d'interrompre l'exécution.
PDF, CSV, MD et JSON, depuis l'en-tête de l'UI ou sift export --fmt.
Le PDF est celui à remettre au dossier d'incident : une page de garde avec la cible, les totaux par gravité et par état de tri, les règles les plus fréquentes, les valeurs réutilisées entre fichiers, puis les résultats groupés par gravité. Il est généré directement, sans bibliothèque PDF, donc il fonctionne sur une machine qui n'a jamais vu pip.
Les exports sont expurgés par défaut. Les valeurs sont masquées, chaque page est
marquée d'un bandeau, et le endpoint ne désactive le masquage que sur un
redact=0 explicite - une requête tronquée ou malformée ne peut pas fuiter. Désactiver l'expurgation
dans l'UI exige de confirmer une boîte de dialogue d'avertissement, et le fichier obtenu est
marqué d'un bandeau UNREDACTED - CONTAINS PLAINTEXT CREDENTIALS sur chaque page.
La base des résultats conserve les valeurs réelles, car un analyste doit savoir quel mot de passe a fuité pour savoir quoi faire tourner. La limite, c'est ce qui sort de l'outil.
Tout est cliquable. Le contrôle Tri en haut de la liste des résultats réorganise par gravité, chemin de fichier, règle, état de tri ou plus récent, avec un bouton pour inverser. Les facettes de la barre latérale filtrent par gravité, catégorie, règle et valeur réutilisée. Le tri, l'expansion du contexte, l'ouverture d'un fichier et la création de règles sont tous des boutons.
Les raccourcis clavier ci-dessous sont des accélérateurs pour une longue file, pas la seule façon de la piloter.
Le bouton instantané génère le fragment surligné en PNG pour le coller dans un ticket d'incident ; copier le fragment fait de même en markdown.
L'UI affiche des identifiants en clair en direct dans une fenêtre de navigateur, donc :
127.0.0.1, et refuse toute autre adresse sans --unsafe-bind ;SameSite=Strict ;Host, donc une page hostile ne peut pas faire de DNS-rebind dessus ;/api/context n'est pas une lecture de fichier arbitraire et /api/open n'est pas un
lancement de processus arbitraire.La base des résultats contient des identifiants en clair par conception - un analyste en réponse aux incidents
a besoin de savoir quel mot de passe a fuité pour savoir quoi faire tourner. Traitez
%LOCALAPPDATA%\sift\<target>\findings.db comme une preuve : même traitement que le
partage lui-même, et supprimez-la à la clôture de la mission. Utilisez --redact si elle
doit quitter le périmètre de l'incident.
Et l'évidence : n'exécutez sift que contre des systèmes auxquels vous êtes autorisé à accéder.
sift/rules_builtin.py - règles de contenu. sift/rules_filename.py - règles de nom de
fichier. Les deux sont en Python pur avec des motifs en chaînes brutes, donc lisibles et
diffables ; les règles utilisateur vivent en JSON dans .sift/user-rules.json.
Trois niveaux vous permettent d'échanger précision contre rappel :
cpassword GPP,
dumps NTLM, mots de passe de liaison LDAP. La forme seule fait foi.Une règle peut aussi porter min_digits, min_lowercase, min_uppercase et
min_special, afin qu'une règle bruyante puisse être resserrée sans toucher au
reste, ainsi que examples - des chaînes qu'elle doit continuer à reconnaître.
examples est la moitié utile. sift selftest exécute chaque règle contre les
chaînes pour lesquelles elle a été écrite, sur tout le chemin : correspondance, extraction, puis les
filtres de suppression. Cette dernière étape est celle qui compte, car la
régression qui se produit réellement n'est pas un motif qui cesse de correspondre - c'est un
filtre anti-bruit, resserré pour de bonnes raisons ailleurs, qui dévore silencieusement un vrai
résultat au passage.
Elle justifie immédiatement son existence. L'ajout d'exemples aux règles existantes a révélé
une lacune concrète : un underscore est un caractère de mot, donc le \b initial dans la
règle d'affectation générique refusait de correspondre dans DB_PASSWORD, MYSQL_PASSWORD
ou REDIS_PASSWORD - trois des noms de variables de secrets les plus courants au
monde, silencieusement manqués. L'exemple semblait visiblement correct et ne correspondait
pas, ce qui est exactement son but.
Les règles que vous écrivez dans l'UI bénéficient de cela gratuitement : la ligne que vous avez sélectionnée est enregistrée comme exemple de la règle, donc une règle que vous modifiez six mois plus tard vous indique lorsqu'elle a cessé de correspondre à ce qui vous a poussé à l'écrire.
sift import-rules accepte un .toml de gitleaks, un .yml de style Kingfisher/Titus,
ou un répertoire de ceux-ci. Contre le pack de Kingfisher, cela donne 1 073 des 1 082
règles importées, avec leurs seuils d'entropie, leurs exigences de chiffres et de casse,
et leurs exemples. Le YAML est lu par sift/yamlmini.py, un lecteur pour le
sous-ensemble utilisé par ces packs - sans dépendance, et il lève une exception sur les ancres et les tags
plutôt que de faire semblant de les comprendre.
Deux choses sont volontairement écartées à l'entrée :
validation:, qui nomment une URL par règle. sift ne contacte jamais que des
hôtes codés en dur dans validate.py. Un pack de règles qui pourrait nommer un endpoint
choisirait où vos résultats sont envoyés, et un fichier de règles est une donnée, pas
une décision.[[:alnum:]] est une classe
POSIX ; Python le lit comme un ensemble de caractères littéraux, le compile sans problème,
et correspond à la mauvaise chose. La traduction est vérifiée contre les exemples de chaque
règle, donc un motif qui a survécu à la compilation mais dont le sens a changé est
rejeté au lieu de ne jamais se déclencher en silence. Douze des règles de Kingfisher échouent
à ce contrôle et ne sont pas importées.Le fait que la propre suppression de sift laisserait tomber un exemple donné ne disqualifie pas
la règle - ces packs contiennent volontairement des échantillons factices (keyXXXXXXXX,
...EXAMPLE), donc le filtre de placeholders a raison sur l'exemple et ne dit
rien sur le motif. Être strict à ce sujet aurait écarté 121 règles fonctionnelles
avant que la distinction soit établie.
Les règles génériques de secrets sont abandonnées à cause du bruit, donc les filtres anti-bruit sont réglés aussi soigneusement que les motifs. Mesurées sur un vrai arbre de 6 000 fichiers, les règles de suppression ci-dessous font passer les résultats de 1 088 à 257 sans aucune perte de rappel sur le corpus de test :
password: process.env.DB_PASS est une variable, pas
une valeur. Cette seule suppression élimine la plupart du bruit des règles génériques dans les arbres de code source.def login(user: str, password: str) est une signature..wrangler, .next, site-packages,
node_modules, …) ainsi que les bundles minifiés et les source maps sont ignorés. Le texte généré par
machine ne produit que des faux positifs générés par machine.Il n'existe volontairement aucune règle de contenu https://user:pass@host avec une fin trop permissive.
La version évidente s'est déclenchée 476 fois sur un arbre de dev, car le JSON minifié
n'a pas d'espace blanc et le motif est parti d'une URL à travers guillemets et virgules
jusqu'à ce qu'il trouve un @ sans rapport des centaines de caractères plus loin.
python tests/run_all.py
Douze suites : détection contre un partage synthétique (réparties entre « ce que les scanners CI détectent déjà », « l'écart que cet outil vise » et « les leurres qui doivent rester silencieux »), la ligne de commande, le classement des suggestions de règles, l'enquête et l'acquisition limitée (y compris la mesure de la limite de débit par rapport à l'horloge murale), le générateur de PDF (analysé en retour comme le ferait un lecteur, pour prouver que la rédaction atteint le contenu de la page), les importateurs gitleaks/trufflehog, l'API HTTP y compris chaque garde de sécurité, l'analyse statique de l'interface utilisateur, et le lecteur de blocs pour les fichiers surdimensionnés (affirmant qu'un secret dans le dernier bloc signale toujours son vrai numéro de ligne dans le fichier entier).
Pour obtenir un partage avec lequel jouer :```bash
sift demo C:\temp\demoshare
Chaque identifiant de ce corpus est fabriqué.
Deux niveaux, car ils comportent des risques très différents.
Les sommes de contrôle sont gratuites et toujours activées. ghp_, npm_, Atlassian ATATT,
Bitbucket ATCTT et les jetons routables glpat- de GitLab portent tous un CRC32 sur leur propre
contenu. Le recalculer permet de répondre hors ligne à ce qui nécessitait auparavant Internet :
s'agit-il d'un jeton réel ou de l'exemple que quelqu'un a collé dans un README ? Les résultats sont
étiquetés checksum ok ou malformed dans la file d'attente. Une somme de contrôle prouve la forme, pas
la vie - un jeton bien formé peut avoir été révoqué il y a un an.
La validation en direct est désactivée jusqu'à ce que vous l'exécutiez. sift validate demande au fournisseur
si un identifiant fonctionne toujours. C'est une commande séparée, pas une option sur
scan, et elle vous fait taper validate à une invite qui nomme d'abord chaque point de terminaison qu'elle contactera. Les règles de sécurité :
sift/validate.py. Aucune règle - intégrée,
écrite par l'utilisateur ou importée depuis le pack de quelqu'un d'autre - ne peut fournir une URL.
Sans cela, importer un pack de règles suffirait à envoyer chaque identifiant présent sur le partage
vers une adresse choisie par l'auteur du pack.--redact : si les valeurs sont masquées
parce que la base de données quitte le périmètre d'incident, transmettre le texte en clair est
exactement ce contre quoi l'on se protège.validation-log.json à côté de la base de données des résultats
Les tentatives de validation aboutissent dans les journaux d'audit du propriétaire de l'identifiant, attribuées à votre adresse, à ce moment-là. C'est parfois exactement ce que vous voulez, et parfois cela prévient un adversaire qui surveille. Décidez avant de l'exécuter ; c'est pour cela qu'il demande.
Actuellement GitHub, npm, Slack et Stripe. trufflehog vérifie encore bien plus -
exécutez-le et import-findings pour obtenir les deux.
import-findings ; les résultats vérifiés remontent en tête..kdbx ou .pfx est signalé par
son nom ; sift ne tente pas de l'ouvrir.name=value, sans quoi le mot secret et sa valeur se trouvent dans des colonnes séparées et aucune règle ne voit la paire..7z, .rar et les archives imbriquées sont signalées mais pas extraites. Seuls les formats basés sur ZIP
et les messages .eml sont lus à l'intérieur.--max-size sont lus par blocs, pas mis en cache. Ils sont analysés
(une chaîne de connexion dans un .bak de 4 Go est trouvée, à son vrai numéro de ligne), mais
le cache de texte ne contient que ce qui rentre, donc la relecture en cache d'une seconde ne les couvre pas - une nouvelle règle atteint un fichier volumineux lors de la prochaine analyse complète.Apache-2.0. Voir LICENSE.
Le corpus de test de détection (sift/selftest.py, tests/) contient
exprès des identifiants au format correct mais fictifs ; les alertes de secret-scanning
pour ces chemins sont supprimées via .github/secret_scanning.yml.
| Option | Effet |
|---|
--tier 1|2|3 | Curseur de rappel. 1 = signal élevé, 2 = défaut, 3 = ne rien manquer |
--redact | Masque les valeurs dans le store et les exports. À utiliser si la base quitte le périmètre de l'incident |
--no-ui | Remplit le store puis se termine, pour les exécutions scriptées |
--no-browser | Démarre le serveur UI sans ouvrir de navigateur (utile en RDP) |
--include/--exclude GLOB | Réduit le parcours |
--no-archives | N'ouvre pas les conteneurs docx/xlsx/zip |
--no-strings | N'exécute pas une passe strings sur les binaires |
--no-large | Ignore les fichiers dépassant --max-size au lieu de les lire par blocs |
--jobs N | Processus workers (défaut : auto) |
--max-size MB | Ignore les fichiers au-dessus de cette taille (défaut 25) |
--port N | Port de l'UI (défaut 8973) |
| Touche | Action |
|---|
j / k | résultat suivant / précédent |
↑ / ↓ | élargir le contexte vers le haut / vers le bas |
c / f | confirmer / marquer comme faux positif |
x | basculer la sélection pour le tri groupé |
o / n | ouvrir dans VS Code à la ligne / ouvrir le fichier dans Notepad |
r | créer une règle à partir du texte surligné |
y | copier la valeur |
/ | rechercher |
.doc/.xls/.pdf (pre-2007 et PDF) passent par un balayage de chaînes plutôt que par un
véritable analyseur, donc le rappel sur ceux-ci est plus faible que sur les formats OOXML.