Cos'è e perché conta
Quando colleghi un servizio alla tua app, tipo un database, i pagamenti o un'AI, ti danno una chiave. È una stringa lunga, e la cosa più comoda è incollarla dritta nel codice. Se la passi a Claude in chat senza dirgli dove metterla, a volte lo fa anche lui.
Il problema è dove va a finire il codice. Se lo carichi su GitHub, la chiave viaggia con lui. In un repository pubblico la può leggere chiunque, e ci sono programmi che girano apposta per raccogliere le chiavi appena compaiono. Chi la trova la usa a nome tuo.
E cancellarla dal codice non basta. Git tiene tutta la storia: se la chiave è stata in un commit, anche uno solo, anche di mesi fa, è ancora lì. Chi scarica il repository la ritrova.
Non tutte le chiavi sono segrete. Quella pubblica di Supabase o Firebase è fatta per stare nell'app: ne parlo nella guida PORTA (strikt.fit/porta). Qui parlo delle altre: la chiave segreta del database, quella dell'AI, dei pagamenti, delle email. Quelle stanno solo sul server, come nella guida LIMITE (strikt.fit/limite).
1. Il controllo
Prima di sistemare, guarda cosa c'è. Apri Claude Code nella cartella del progetto e incolla:
Cerca nel progetto e in tutta la cronologia git chiavi, token e password scritti nel codice, anche nei file di configurazione e nei commit vecchi. Per ognuno dimmi file, riga (o commit) e servizio. Non stampare mai il valore.
«In tutta la cronologia», se no Claude guarda solo i file di adesso. «Non stampare mai il valore», perché ti basta sapere dove sta: la chiave non deve comparire nella chat.
Un secondo parere: gitleaks
Un programma fatto apposta riconosce la forma di tantissimi tipi di chiave. Io uso gitleaks, che è open source. Sul Mac lo installi con Homebrew:
brew install gitleaks
Poi, dentro la cartella del progetto:
gitleaks git -v --redact gitleaks dir -v --redact
- gitleaks git guarda tutta la cronologia, commit per commit.
- gitleaks dir guarda i file di adesso, anche quelli che git non segue.
- --redact nasconde i valori nei risultati, -v mostra i dettagli.
Su GitHub
Nei repository pubblici la secret scanning di GitHub è gratis e automatica: se trova la chiave di un servizio che conosce, mette un avviso nella scheda Security and quality. La push protection fa di più: blocca il push che contiene una chiave, prima che arrivi online. Per i tuoi push sui repository pubblici è attiva di default; sul repository la accendi da Settings → Advanced Security. Sui repository privati potrebbe richiedere una funzione a pagamento di GitHub.
2. Le chiavi in .env
Il posto giusto per una chiave è un file .env che git non carica mai. Funziona così:
- Le chiavi vanno in un file .env nella cartella del progetto: NOME_CHIAVE=valore.
- Nel file .gitignore aggiungi la riga .env: git non lo carica mai.
- Nel codice resta solo il nome: process.env.NOME_CHIAVE (il tuo framework o dotenv legge il file).
- Dove pubblichi l'app, le stesse variabili vanno nelle impostazioni dell'hosting.
Il prompt per farlo fare a Claude:
Sposta le chiavi fuori dal codice: mettile in .env, aggiungi .env al .gitignore, crea un .env.example con i soli nomi e controlla che nessuna chiave segreta finisca nel codice del browser.
Il .env.example invece lo carichi: ha solo i nomi, tipo AI_API_KEY= senza niente dopo. Così più avanti tu, e chi lavora con te, sapete quali chiavi servono senza vederne nessuna.
Se .env era già nel repository
Il .gitignore vale solo per i file che git non segue ancora. Se .env era già stato caricato, .gitignore non basta: lancia git rm --cached .env, che lo toglie dal repository e lo lascia sul computer, poi fai un commit. Poi cambia le chiavi che c'erano dentro, perché restano nella cronologia: vedi la parte 4. Se vuoi, chiedi a Claude di farlo e di spiegarti ogni passo.
3. Le variabili che finiscono nel browser
Qui si cade più spesso. Mettere una chiave in .env non la rende segreta: conta dove gira il codice che la usa. Quello che gira sul server resta tuo. Quello che arriva nel browser, o nell'app sul telefono, lo vede chiunque, .env o no.
Alcuni framework mandano apposta al browser le variabili con un certo prefisso:
- Next.js: NEXT_PUBLIC_. Il valore viene scritto dentro il JavaScript che arriva al browser.
- Vite: VITE_. La documentazione di Vite dice esplicitamente di non metterci chiavi.
- Expo: EXPO_PUBLIC_. Il valore resta in chiaro nell'app compilata.
Una chiave segreta con uno di questi prefissi è pubblica, anche se sta in .env. Succede così: la chiamata all'AI dal browser non funziona, si aggiunge il prefisso, adesso funziona, e la chiave è uscita. La soluzione giusta è spostare la chiamata sul server.
Se non vuoi che Claude legga .env
Claude Code può leggere i file della cartella, .env compreso. Se preferisci di no, aggiungi una regola di blocco in .claude/settings.json, nella cartella del progetto:
{
"permissions": {
"deny": ["Read(./.env)"]
}
}Mettila dopo aver spostato le chiavi: blocca anche la scrittura di quel file. Vale per gli strumenti con cui Claude legge i file e per comandi come cat, non per uno script che apre il file da solo: per quello serve la sandbox di Claude Code.
4. Se una ti è già scappata
Se trovi una chiave segreta in un commit, su GitHub o nel codice che va nel browser, considerala bruciata. Non sai chi l'ha già copiata. Oggi, in quest'ordine:
- Sul sito del servizio revoca la chiave e creane una nuova. È il passo che conta: chi l'ha copiata la può ancora usare.
- Metti la nuova solo in .env e nelle impostazioni dell'hosting.
- Controlla sul servizio i consumi strani.
- Poi, con calma, pulisci la cronologia.
Se l'app è online puoi creare la chiave nuova un attimo prima di revocare la vecchia, così non resta ferma. Ma la vecchia la revochi oggi, non domani. Per l'ultimo passo, chiedi prima una spiegazione:
Una chiave è finita in un commit vecchio. Spiegami come toglierla dalla cronologia git prima di toccare qualunque cosa.
Perché questo ordine. Togliere una chiave dalla cronologia vuol dire riscrivere la storia del repository (GitHub consiglia git-filter-repo) e caricarla con un force push, e le copie vecchie e i fork la contengono ancora. Per questo GitHub mette come primo passo revocare la chiave, e dice che a volte basta: una chiave revocata non apre più niente.
Gli errori più comuni
- Cancellare la chiave dal codice e fermarsi lì. Resta nella cronologia: revocala e creane una nuova.
- Pulire la cronologia prima di revocare la chiave. Revocala subito: la pulizia viene dopo.
- Aggiungere .env al .gitignore quando era già stato caricato. Toglilo con git rm --cached .env e un commit, poi cambia le chiavi che c'erano dentro.
- Mettere una chiave segreta in una variabile NEXT_PUBLIC_, VITE_ o EXPO_PUBLIC_. Finisce nel browser o nell'app: sposta la chiamata sul server.
- Incollare le chiavi nuove nella chat con Claude. Mettile tu in .env e digli solo il nome della variabile.
Checklist
- Controllo fatto su file e cronologia, con Claude e con gitleaks.
- Le chiavi segrete stanno solo in .env e nelle impostazioni dell'hosting.
- .env è nel .gitignore e non è nel repository.
- C'è un .env.example con i nomi, senza valori.
- Nessuna chiave segreta in variabili NEXT_PUBLIC_, VITE_ o EXPO_PUBLIC_.
- Ogni chiave scappata è stata revocata e sostituita.
- Push protection attiva sul repository GitHub.
Domande frequenti
Il mio repository è privato. Devo farlo lo stesso?
Sì. Se un giorno lo rendi pubblico o aggiungi qualcuno, la cronologia arriva intera, chiavi comprese. E le variabili che finiscono nel browser sono pubbliche comunque.
La chiave pubblica di Supabase in una variabile NEXT_PUBLIC_ va bene?
Sì, è fatta per stare nel browser. A proteggere i dati sono le regole sul database, come spiego nella guida PORTA. La chiave segreta (secret o service_role) invece mai.
Ho già revocato la chiave. Devo comunque pulire la cronologia?
Non sempre: una chiave revocata non apre più niente. Se nella cronologia c'è altro di sensibile, tipo dati di persone, o non sei sicuro di aver revocato tutto, pulisci, partendo dal prompt della parte 4.