Vai al contenuto
CodeAuditAgent
Tutti gli articoli

Segreti trapelati nella cronologia Git: trovare, ruotare, prevenire

Cancellare una chiave API committata non la rimuove dalla cronologia Git. Come trovare i segreti trapelati, ruotarli, ripulire la cronologia e prevenire i leak.

· 7 min di lettura · Lina Source LLC

Qualcuno committa un file .env, oppure incolla una chiave API attiva in un modulo di configurazione per provare rapidamente una cosa. Un revisore se ne accorge, la chiave viene cancellata nel commit successivo e tutti vanno avanti. La chiave è ancora lì. Git conserva ogni versione di ogni file, e chiunque possa clonare il repository può leggere il commit che l'ha aggiunta.

Le credenziali hardcoded sono classificate come CWE-798. Questa guida spiega cosa fare quando una di esse è già finita nella cronologia: trovare ogni segreto, ruotarli prima di qualsiasi altra cosa, decidere se riscrivere la cronologia ne valga la pena e predisporre le protezioni che fermano il prossimo.

Perché cancellare la riga non basta

Un commit che rimuove un segreto aggiunge un nuovo snapshot privo di esso. Lo snapshot precedente, con il segreto, è ancora referenziato dalla cronologia del branch. git log -p lo mostra, un git checkout del vecchio commit lo ripristina, e ogni clone e ogni fork esistente ne ha già una copia. Nemmeno fare lo squash del branch prima del merge aiuta, se i commit originali sono stati pushati: il remote potrebbe conservarli ancora, e chiunque li abbia recuperati li ha in locale.

Su un repository pubblico, dai per scontato che il segreto sia stato visto. Gli scraper automatici sorvegliano i push pubblici alla ricerca di pattern di credenziali, e la finestra tra un push e il primo abuso può essere brevissima. Su un repository privato l'esposizione è minore ma non nulla: ogni dipendente, collaboratore esterno, sistema di CI e integrazione con accesso in lettura ce l'ha.

Passo 1: ruota per prima cosa

La rotazione è l'unico passo che elimina davvero il rischio. Riscrivere la cronologia nasconde il segreto ai cloni futuri; non fa nulla per le copie che già esistono. Quindi revoca e sostituisci la credenziale prima di toccare il repository.

Pianifica la rotazione in modo che non causi un'interruzione del servizio. Molti provider consentono a due chiavi di essere valide contemporaneamente: crea la nuova chiave, distribuiscila ovunque fosse usata la vecchia, verifica che il traffico si sia spostato e solo allora revoca la vecchia. Se il provider consente una sola chiave, accetta una breve interruzione; qualche minuto di downtime è un compromesso migliore che lasciare attiva una credenziale notoriamente trapelata mentre coordini un passaggio perfetto.

  • Genera una nuova chiave nella console del provider e distribuiscila attraverso il tuo normale percorso di configurazione.
  • Revoca la vecchia chiave. Non limitarti a smettere di usarla: una chiave valida inutilizzata è comunque una chiave valida.
  • Controlla i log di audit del provider per individuare attività svolte con la vecchia chiave dalla data del commit in poi: chiamate API, nuovi utenti, permessi modificati, addebiti inattesi.
  • Se il segreto era una password di database o una chiave di firma, considera che cosa avrebbe potuto sbloccare. Un segreto di firma JWT trapelato significa che qualsiasi token avrebbe potuto essere falsificato, quindi invalida le sessioni esistenti.
  • Metti per iscritto che cosa è stato esposto, per quanto tempo e che cosa hai cambiato. Ti servirà se un cliente o un auditor lo chiederà.

Passo 2: trova tutto ciò che è trapelato

Dove c'è un segreto committato spesso ce ne sono altri. Cerca nell'intera cronologia, non solo nell'albero attuale, e includi ogni branch e ogni tag.

# Commit che hanno aggiunto o rimosso una stringa in qualsiasi punto della cronologia
git log -p -S "sk_live_" --all

# Ricerca regex nei diff di tutti i commit
git log -p -G "AKIA[0-9A-Z]{16}" --all

# File esistiti in qualche momento in un percorso sospetto
git log --all --oneline -- .env config/production.json

# Gli scanner dedicati controllano centinaia di formati noti di credenziali
gitleaks git -v .
trufflehog git file://. --only-verified

gitleaks e trufflehog sono scanner open source creati per questo compito. Conoscono i formati delle chiavi dei provider più comuni, e trufflehog può verificare se una credenziale trovata è ancora attiva. Le release più vecchie di gitleaks usano gitleaks detect --source . al posto del sottocomando git. Esegui uno scanner una volta sull'intera cronologia, poi tienilo attivo sui nuovi commit. Aspettati qualche falso positivo, come fixture di test e chiavi di esempio nella documentazione. Esamina ciascuno, poi registra i falsi positivi confermati nella allowlist o nel file di baseline dello scanner, così l'esecuzione successiva mostrerà solo i nuovi risultati.

Non dimenticare i luoghi attorno al repository: log di CI che hanno stampato variabili d'ambiente, commenti su issue e pull request, pagine wiki, layer di immagini Docker costruite dal repository e gist o incollati condivisi durante il debug.

Passo 3: decidi se riscrivere la cronologia

Una volta ruotato, il segreto è inutile, quindi riscrivere la cronologia è più una pulizia che un contenimento. Vale comunque la pena farlo quando il repository è pubblico, quando il segreto rivela qualcosa oltre a sé stesso (un hostname interno, il nome di un cliente) o quando la conformità lo richiede. Ha costi concreti: ogni collaboratore deve riclonare, le pull request aperte vengono compromesse e gli hash dei commit cambiano. Qualsiasi cosa faccia riferimento a un commit tramite hash, come note di rilascio, registri di deploy, link a issue o dipendenze fissate in altri repository, punterà a commit che sul branch non esistono più. Annuncia in anticipo la riscrittura, scegli un momento tranquillo e chiudi o fai il merge delle pull request aperte prima di procedere.

git filter-repo è lo strumento che il progetto Git raccomanda per questo, al posto del più vecchio git filter-branch. Lavora su un clone appena creato e rimuove il remote origin come misura di sicurezza, quindi dovrai aggiungerlo di nuovo prima di fare il push.

# Parti da un clone appena creato
git clone [email protected]:acme/api.git api-clean
cd api-clean

# Opzione A: rimuovi un file da ogni commit
git filter-repo --invert-paths --path config/production.env

# Opzione B: sostituisci le stringhe segrete ovunque compaiano
# replacements.txt contiene una regola per riga, per esempio:
#   PASTE_THE_OLD_KEY_HERE==>REDACTED
git filter-repo --replace-text ../replacements.txt

# filter-repo rimuove origin; riaggiungilo ed esegui un force-push
git remote add origin [email protected]:acme/api.git
git push --force --all
git push --force --tags

Che cosa la riscrittura non può fare

  • Non può raggiungere cloni, fork, cache di CI o backup già esistenti. Chi ha fatto fetch prima della riscrittura conserva i vecchi commit.
  • Su GitHub, i riferimenti creati per le pull request sono di sola lettura e possono mantenere raggiungibili i vecchi commit. Per rimuovere le viste in cache e quei riferimenti occorre contattare il supporto GitHub.
  • I collaboratori che fanno pull o merge da un vecchio clone possono ripubblicare direttamente la vecchia cronologia. Chiedi a tutti di riclonare e proteggi il branch mentre lo fanno.
  • Non rimuove il segreto da nessun altro posto in cui è stato copiato: log, ticket, messaggi di chat o artefatti di build.

Ecco perché l'ordine conta. Prima ruota, poi pulisci. Una cronologia riscritta con una chiave ancora attiva è un falso senso di sicurezza. Dopo il push, esegui il tuo scanner su un clone appena creato per confermare che il segreto sia davvero scomparso da ogni branch e da ogni tag.

Passo 4: previeni il prossimo

L'obiettivo è rendere difficile committare un segreto e rapido accorgersene. Nessun singolo controllo fa entrambe le cose, quindi stratificali.

Tieni i segreti fuori dal percorso del codice

Leggi le credenziali da variabili d'ambiente o da un secret manager a runtime, e verifica all'avvio che quelle necessarie siano presenti. Committa un file .env.example con valori segnaposto e metti .env nel .gitignore fin dal primo commit. Per la produzione, un secret manager come AWS Secrets Manager, Google Secret Manager, HashiCorp Vault o le impostazioni d'ambiente cifrate della tua piattaforma ti offre controllo degli accessi, log di audit e una rotazione più semplice.

Blocca i segreti prima che vengano committati

Un hook di pre-commit intercetta l'errore sulla macchina dello sviluppatore, prima che arrivi al remote. Il framework pre-commit riduce tutto questo a poche righe di configurazione che ogni contributor può installare.

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: vX.Y.Z  # fissa il tag dell'ultima release
    hooks:
      - id: gitleaks

# Installa una volta per clone
#   pip install pre-commit
#   pre-commit install

Gli hook sono locali e possono essere saltati, quindi esegui lo stesso scanner in CI a ogni push come rete di sicurezza. Su GitHub, attiva il secret scanning e la push protection dove il tuo piano li supporta; la push protection rifiuta lato server i push che contengono token di provider riconosciuti.

Riduci il danno dei leak che ti sfuggono

  • Limita ogni chiave ai permessi minimi e, dove il provider lo consente, a IP o referrer specifici.
  • Preferisci credenziali di breve durata, come la federazione OIDC dalla CI al tuo cloud provider, alle chiavi statiche di lunga durata.
  • Usa chiavi separate per ogni ambiente, così una chiave di test trapelata non può toccare la produzione.
  • Tieni un runbook di rotazione per ogni segreto critico, così ruotarlo sotto pressione diventa routine anziché improvvisazione.

Dove si inserisce la code review

Gli scanner riconoscono bene i formati noti; sono più deboli sul contesto, come una password assemblata da due costanti o una credenziale predefinita presente in un fallback di configurazione. Una passata di revisione sul codice individua questi casi. CodeAuditAgent segnala le credenziali hardcoded come CWE-798 con le prove citate quando analizza il branch predefinito di un repository pubblico o uno snippet incollato. Legge il codice attuale, non la cronologia dei commit, quindi mantieni attivo uno scanner della cronologia per i commit passati.

In breve: un segreto committato è un segreto trapelato. Ruotalo oggi stesso, pulisci la cronologia se vale il disagio e fai in modo che il prossimo leak fallisca sull'hook di pre-commit anziché finire su una pagina pubblica.