<link rel="stylesheet" href="/assets/fonts/jetbrains-mono/jetbrains-mono.css" />
All posts

Password finite nella history di git: come le ho rimosse (e perché non basta)

Facevo una revisione generale del mio progetto, alla ricerca di cose dimenticate. Ne ho trovata una che non avrei voluto: un file di configurazione per il deploy, con dentro la chiave che firma i token di login, la password dell'amministratore e la password per l'invio delle email. In chiaro, nella history di git, su tre branch.

Com'è potuto succedere

Il file era nel .gitignore. Il punto è che ci era finito dopo essere stato committato una prima volta, e .gitignore ignora solo i file non ancora tracciati: un file già tracciato continua a esserlo, modifica dopo modifica, anche se il suo nome compare nella lista.

Il repository era privato, ma privato non vuol dire sicuro: lo vedono i collaboratori, i servizi di CI, i backup e ogni computer su cui è stato clonato.

Rimuoverlo da tutta la history

Cancellare il file con un nuovo commit non serve: resta in tutti i commit precedenti. Bisogna riscrivere la history di ogni branch. Ho usato git filter-repo su un clone mirror, poi ho forzato il push e verificato che nessun commit lo contenesse più:

# 1. Stop tracking the file (it stays on disk)
git rm --cached .env.deploy
git commit -m "Stop tracking .env.deploy"

# 2. Rewrite the history of EVERY branch on a mirror clone
git clone --mirror git@github.com:me/my-repo.git
cd my-repo.git
git filter-repo --path .env.deploy --invert-paths
git push --force --mirror

# 3. Verify: no commit on any branch may still contain it
git log --all --oneline -- .env.deploy

Dopo una riscrittura del genere, chiunque abbia una copia locale deve riallinearla al nuovo remoto: i vecchi commit non vanno più ripubblicati.

Perché non basta

Qui sta la lezione più importante. Anche dopo la riscrittura, i vecchi commit possono restare raggiungibili: GitHub, per esempio, mantiene dei riferimenti interni alle pull request che un force push non può cancellare. E non si sa chi abbia già scaricato il repository in passato.

Rimuovere un segreto dalla history serve a non peggiorare la situazione; l'unica cosa che la risolve è cambiarlo. In pratica:

  • Nuova chiave di firma dei token: invalida tutte le sessioni esistenti, quindi tutti dovranno rifare il login. È il prezzo da pagare.
  • Nuova password dell'amministratore.
  • Revoca della password per l'app di posta e generazione di una nuova.
  • Aggiornamento delle variabili su ogni ambiente in cui girano.

La seconda volta, stesso schema

Qualche settimana dopo ho trovato lo stesso problema in un'altra forma: uno script di installazione del server, comodissimo, che scriveva le credenziali direttamente nel file di configurazione. È lo schema ricorrente: lo script di comodità che contiene i segreti per non dover configurare niente. L'ho riscritto perché generi le chiavi al momento e chieda le password a chi lo esegue:

# Before: secrets written into a script tracked by git
# JWT_SECRET=hardcoded-value...

# After: generated or asked at runtime, never committed
JWT_SECRET="$(openssl rand -base64 48)"
read -rsp "SMTP password: " SMTP_PASS; echo
umask 077   # the .env file is readable by its owner only
printf 'JWT_SECRET=%s\nSMTP_PASS=%s\n' "$JWT_SECRET" "$SMTP_PASS" > .env

Le abitudini che evitano che succeda di nuovo

  1. Un .env.example committato, con i nomi delle variabili e nessun valore; il .env vero non entra mai nel repository.
  2. Uno scanner di segreti prima di ogni commit, come gitleaks in un hook di pre-commit: blocca il commit prima che il danno sia fatto.
  3. La protezione dei push offerta da GitHub e GitLab, che rifiuta i push contenenti segreti riconoscibili.
  4. Mai segreti negli script: generarli a runtime o leggerli dall'ambiente.
  5. Un controllo periodico della history: un segreto trovato da te è un incidente piccolo, uno trovato da altri no.

In sintesi

Un segreto in un repository va trattato come compromesso nel momento stesso in cui lo trovi. Riscrivere la history è necessario ma non sufficiente: la vera correzione è cambiare ogni credenziale esposta. Poi conviene rendere il problema impossibile da ripetere, con scanner e regole chiare. Ho raccolto altre pratiche di sicurezza in questa guida.

💬 Note dei lettori

0 note

Scrivi una nota

Condividi la tua opinione, un suggerimento o un complimento

Ultime note

Nessuna nota ancora. Sii il primo a commentare!