“Attaccare con lo scotch la chiave di casa alla porta d'ingresso perché avevi fretta.”
Il codice client non ha segreti
Tutto ciò che il tuo browser esegue, il tuo utente può leggerlo. Il bundle JavaScript viene scaricato sulla sua macchina; minificarlo non è cifrarlo. Qualsiasi cosa metti nel codice front-end (una chiave in una variabile, un token in una config, un segreto in una variabile d'ambiente esposta al client) è visibile a chiunque apra gli strumenti sviluppatore. Non esiste "nascosto" nel browser.
Questo inganna molti perché i framework hanno una convenzione che sembra una funzione di sicurezza ma non lo è. Un prefisso come NEXT_PUBLIC_ (o VITE_, o REACT_APP_) non protegge un valore; fa l'opposto. Marca la variabile come una che verrà inclusa nel bundle client e spedita al browser. Il prefisso significa "questo è pubblico". Uno strumento IA che vi ricorre per "sistemare" una variabile undefined sta pubblicando in silenzio il tuo segreto.
Due tipi di chiavi, e la confusione fatale
La maggior parte dei servizi che contano ti dà due tipi di credenziale, e tutto il gioco sta nel non confonderli:
- Chiavi publishable / anon / client: progettate per essere pubbliche. La chiave publishable di Stripe, la chiave anon di Supabase. Sicure nel browser perché, da sole, non possono fare nulla di privilegiato (si affidano a regole server come la RLS per essere sicure).
- Chiavi secret / service / server: pieni poteri. La chiave segreta di Stripe, una chiave OpenAI, una chiave service-role del database. Possono addebitare carte, consumare il tuo budget di modello o leggere ogni riga. Non devono mai toccare il browser, un repo pubblico o un prompt.
Già trapelata? Ruotala: non si può de-trapelare.
Se un segreto è stato nel codice client, in un repo pubblico, in un log o in una chat, è bruciato. Cancellare la riga non aiuta: è ancora nella storia di git, nella cache di qualcuno, nel database di uno scraper. L'unico vero rimedio è ruotare, revocando la vecchia chiave presso il provider ed emettendone una nuova, e poi conservare quella nuova nel modo corretto. Nascondere una chiave trapelata non è una remediation; ruotarla lo è.
# .gitignore: keep secrets out of the repo in the first place .env .env.local .env.*.local # Already committed once? It's in history. Rotate the key at the provider, # then scan history so you know what else escaped: # npx gitleaks detect --source . --redact
Dove devono vivere le chiavi
Il pattern che tiene: i segreti vivono sul server, e il browser chiede al server di usarli per suo conto. Invece di far chiamare al browser un'API di terze parti con una chiave segreta, il browser chiama il tuo endpoint, e il tuo server, che l'utente non può leggere, aggiunge la chiave ed effettua la chiamata. La chiave non attraversa mai il confine verso codice che l'utente controlla.
- Variabili d'ambiente server o un secrets manager: mai un file nel repo, mai un prefisso esposto al client.
- Una route server sottile che fa da proxy verso le API di terze parti, così la chiave segreta resta lato server (la preferenza di SDEN per backend reali rispetto a stack solo-client nasce in parte da qui).
- Mai nel bundle del browser, mai su git, mai incollata in un prompt IA o in un documento condiviso.
Una volta interiorizzato che "il browser è pubblico", la maggior parte delle decisioni sulla gestione dei segreti si risponde da sola: se la fuga di un valore farebbe danno, quel valore va sul server, punto.
Una riga per ciascuno
- Tutto ciò che sta nel browser è leggibile; un prefisso NEXT_PUBLIC_/VITE_ pubblica un valore, non lo protegge.
- Le chiavi publishable sono sicure nel client per progettazione; le chiavi secret/service (Stripe, OpenAI, service-role) non lo sono mai.
- Un segreto trapelato non si può de-trapelare: ruotalo presso il provider; cancellare la riga lo lascia nella storia di git e negli scraper.
- Le chiavi stanno sul server: env/secrets manager più un proxy sottile, mai nel bundle, su git o in un prompt.
Dove andare ora