Basi della sicurezza
Il tuo file .env è esposto? I dodici percorsi da provare
Il tuo file .env è esposto sul tuo server web? Dodici indirizzi te lo dicono, e una risposta significa che tutto quello che contiene è già pubblico.

In breve
- Due problemi diversi si chiamano allo stesso modo: un file .env esposto. Questo è il file stesso che sta sul tuo server web, scaricabile da chiunque scriva l’indirizzo.
- Un host che serve solo il tuo frontend già costruito non può farlo. Un server vero sì, ed è per questo che sette delle otto app che abbiamo trovato erano app Replit.
- Su 30.761 app online dove il nostro controllo ha avuto una risposta, 8 consegnavano un file privato. Dodici indirizzi ti dicono se sei la nona.
Qualcuno ti ha detto che il tuo file .env è esposto, e dalla frase non capisci
se sia grave. Copre due situazioni completamente diverse. Una è lo strumento che
funziona come deve. L’altra significa che un file che credevi privato si scarica
dal tuo sito online, da chiunque, da quando sta lì.
Ecco la parte che guida dopo guida mette insieme: un valore del tuo .env che
finisce dentro il tuo JavaScript non è lo stesso fatto del file .env servito
dal tuo server web. Il primo è il build che fa quello che gli hai chiesto
quando hai chiamato la variabile VITE_QUALCOSA. Il secondo è un errore di
archiviazione, ed è quello da controllare per primo, perché puoi controllarlo tu
da fuori in circa un minuto.
Pensa al tuo sito online come al bancone di un negozio. Tutto quello che sta sul
bancone è lì per essere preso: la pagina, le immagini, il JavaScript, il logo. Il
retro contiene quello che fa funzionare il negozio, e un visitatore non ha
nessuna strada per arrivarci. Un valore compilato dentro il tuo JavaScript è una
riga stampata sul volantino sul bancone. Il file .env che risponde a un
indirizzo pubblico è la cartella del retro, lasciata sul bancone insieme a tutto
il resto.
Il mio file .env è esposto?
Apri il tuo sito online in un browser, aggiungi /.env in fondo all’indirizzo e
premi invio.
Possono tornare tre cose, e una sola è un problema.
Una pagina della tua app. La maggior parte dei frontend risponde a ogni indirizzo sconosciuto con la propria pagina indice, perché è così che instrada un’app a pagina singola. Ti esce la home, o la schermata di pagina non trovata. Su quel percorso non viene servito niente.
Un errore. Un 403 o un 404 significa che al server è stato chiesto e ha rifiutato. È la risposta che vuoi.
Testo semplice, con righe dentro. Qualcosa come SUPABASE_URL=https://… e
SUPABASE_SERVICE_ROLE_KEY=eyJ…, in un muro monospaziato senza alcuno stile. Il
file viene consegnato a chiunque lo chieda, e lo fa dal giorno in cui è stato
servito la prima volta.
Due cose diverse si chiamano file .env esposto
Una riguarda il tuo bundle. L’altra riguarda il tuo server.
| Cos’è successo | Dov’è il valore | Quanto costa rimediare |
|---|---|---|
Hai chiamato una variabile VITE_, NEXT_PUBLIC_ o EXPO_PUBLIC_ | Compilato nel JavaScript che scarica ogni visitatore | Spostare il lavoro su un server, poi ruotare la chiave. Il file non è mai stato servito. |
| Il tuo server web pubblica la cartella del progetto | Nel file, su https://tuosito/.env | Togliere il file da quello che il server pubblica, poi ruotare tutto il contenuto. |
La prima riga è il caso comune e ha un suo articolo: il prefisso è un’istruzione a pubblicare, e il build l’ha seguita. Tutto quello che segue è la seconda riga.
Perché un’app Lovable non può e una Replit sì
Perché i due host pubblicano cose diverse.
Un builder che fa il deploy di un frontend statico consegna al suo host una
cartella di file costruiti: un po’ di HTML, un po’ di JavaScript, qualche
immagine. Il tuo .env è stato letto durante il build e in quella cartella non
c’è, quindi su /.env non c’è niente da prendere. L’host non potrebbe servire il
file nemmeno volendo.
Un’app Replit di solito fa girare un server suo, nella sua cartella di progetto, con i tuoi file accanto al tuo codice. Un server a cui si dice di servire la sua cartella serve ogni file che ci sta dentro, e non ha modo di sapere che uno di quelli contiene le tue chiavi. Lo stesso vale per qualunque cosa tu abbia messo su una macchina tua.
I numeri seguono l’architettura. Su 30.761 app online dove questo controllo ha avuto una risposta, 8 consegnavano almeno un file privato. Sette delle otto erano app Replit, e le app Replit erano 3.025 delle 30.761. L’ottava stava su un dominio suo, cosa che dice lo stesso in un altro modo: qualcuno faceva girare un server proprio.
È il rilievo più raro che stampiamo, e l’unico dei nove senza una spiegazione innocente. Se vuoi la lettura larga di cosa consegnano davvero le app Replit, ne abbiamo scansionate 3.042.
I dodici percorsi da provare
Dodici indirizzi, nell’ordine in cui conviene. Metti ognuno dopo il tuo dominio.
| Indirizzo | Cosa contiene | Se risponde con del testo |
|---|---|---|
/.env | Ogni chiave con cui la tua app è stata costruita | Tratta tutto come pubblico e ruota |
/.env.local | Lo stesso, da un’esecuzione locale | Lo stesso |
/.env.production | Lo stesso, dal tuo deploy online | Lo stesso |
/.git/config | L’indirizzo remoto del tuo repository | Di solito tutta la directory .git è leggibile |
/.git/HEAD | Su che branch sei | Lo stesso, ed è il più silenzioso dei dodici |
/.aws/credentials | Chiavi Amazon a lunga durata | Ruota su Amazon, poi guarda la fattura |
/database.sql | Schema e righe | Tutte le righe di tutte le tabelle sono pubbliche |
/dump.sql | Lo stesso | Lo stesso |
/backup.sql | Lo stesso | Lo stesso |
/config.json | Quello che ci hai messo dentro | Leggilo e guarda; i token ci stanno più spesso di quanto si pensi |
/docker-compose.yml | Definizioni di servizi, spesso con password dentro | Ruota tutto quello che ci è scritto |
/.npmrc | Un token di registro | Revoca il token |
La nostra scansione legge tutti e dodici da fuori e ti nomina quelli che hanno risposto. Prende una ventina di secondi e non chiede un account: scansiona la tua app.
Cosa significa davvero una risposta
Tutto quello che sta in quel file è pubblico adesso, e lo è dal giorno in cui è stato servito la prima volta.
Non scoprirai chi l’ha letto. Un builder non ti dà nessun log di file prelevati che tu possa andare a guardare, e la richiesta somiglia a qualunque altra richiesta per qualunque altro file. Quello di cui puoi essere certo è che qualcuno ci ha provato: ci sono crawler automatici che percorrono esattamente questi dodici indirizzi su tutta internet, senza sosta, e non hanno bisogno di sapere chi sei per trovare il tuo.
Cinque delle otto app che abbiamo trovato consegnavano una delle quattro che
costano subito: .env, una directory .git, .aws/credentials o un dump
.sql. Le altre tre consegnavano config.json, docker-compose.yml o
.npmrc. Quei tre sono quelli che tutti danno per innocui, ed è esattamente il
motivo per cui finiscono per contenere token e password di database.
Quello che tutto questo non è, è un verdetto sulla tua app. Un controllo esterno legge cosa il tuo sito consegna a uno sconosciuto, e dodici percorsi che rispondono con niente sono dodici percorsi che rispondono con niente. Cos’altro esce da un’app vibe-coded è l’elenco lungo.
Quello che rovina una settimana: una directory .git
Una directory .git non è un file. È tutta la tua cronologia.
Ogni commit sta lì dentro, compreso quello in cui hai incollato una chiave e
quello dopo in cui l’hai tolta. È la parte che la gente capisce male di una
fuga .git: cancellare un segreto dal codice che hai oggi non fa niente alla
versione in cui c’era ancora, e quella versione sta nella stessa directory che il
tuo server pubblica.
Il controllo legge /.git/config e /.git/HEAD perché sono piccoli, il loro
contenuto non si confonde, e uno qualsiasi dei due che risponde significa che la
directory stessa viene servita. Da lì in poi chi legge non ha bisogno di nessuno
strumento speciale. Il formato è documentato e software qualunque lo clona.
Se una chiave è stata in un commit, ruotarla è l’unica cosa che aiuta. Quale venga prima dipende da se la copia è già fuori, e quell’ordine vale la pena azzeccarlo.
Il dump che qualcuno ha lasciato nella cartella
Tre dei dodici percorsi sono dump di database: /database.sql, /dump.sql e
/backup.sql.
Un dump è ogni riga di ogni tabella in un file, con lo schema sopra. Indirizzi email, password con hash, ordini, messaggi, qualunque cosa la tua app conservi. Risponde a un indirizzo pubblico per il motivo più noioso di tutto questo articolo: qualcuno ha fatto la cosa responsabile, ha preso una copia del suo database e l’ha salvata nella cartella di progetto in cui si trovava.
Così il file fatto per proteggere i dati è diventato la via più rapida per leggerli tutti. Dove vive una copia è decisione tanto quanto il farla. Tre modi per fare il backup di un database Supabase passa in rassegna le opzioni, e Reeve Care tiene una copia del tuo database Supabase fuori dal tuo server, verificata prima di contare come backup.
Come sistemarlo, e perché l’ordine conta
Togli il file da quello che il tuo server pubblica. Poi ruota ogni segreto che conteneva. In quest’ordine.
Ruotare per primo sembra la metà urgente, ed è la metà che spreca il lavoro. Le tue chiavi nuove vanno nello stesso file, il file risponde ancora allo stesso indirizzo, e hai ruotato dritto dentro la fuga. Niente è più sicuro di dieci minuti fa.
Dove metterlo invece, a seconda di cosa fai girare:
- Un host con un suo archivio di segreti. Replit Secrets, le variabili d’ambiente di una piattaforma, il pannello del tuo hosting. Il valore viene letto dal tuo server a runtime e non sta mai in un file dentro la cartella pubblicata.
- Fuori dalla cartella servita. Se controlli la configurazione del server, puntalo a una cartella di output del build invece che alla radice del progetto. Così un file nella radice non ha nessun indirizzo.
- Nemmeno nel repository, che è un’abitudine a parte e buona. Per il problema di oggi non fa niente.
L’ultimo punto va detto chiaro, perché .gitignore è la risposta a cui tutti si
aggrappano. Tiene il file fuori dal tuo repository. Il file sul tuo server ci è
finito perché il server sta seduto nella tua cartella di progetto, e git non ha
opinioni al riguardo.
Cosa fare adesso
Cosa fare
- Scrivi tutti e dodici gli indirizzi dopo il tuo dominio, oppure lancia la scansione gratuita e lascia scrivere lei. Leggi cosa torna, non il codice di stato.
- Se uno risponde con le tue impostazioni, togli il file dalla cartella che il tuo server pubblica e rifai il deploy. È il passo che lo ferma.
- Poi ruota ogni chiave, password e token che il file conteneva, presso ogni fornitore. Ruotare prima di spostare il file rimette i valori nuovi dove stavano i vecchi.
- Guarda fatturazione e log del fornitore dopo. Ruotare ferma quello che viene dopo e non fa niente a quello che è già successo.
- Se una directory
.gitera leggibile, ruota tutto quello che è mai stato in un commit, non solo quello che oggi sta nel codice.
Se preferisci passare la tua app come una lista, la checklist di sicurezza in 10 minuti copre questo insieme alle altre cose da chiudere in un’app appena lanciata.
FAQ
Come faccio a sapere se il mio file .env è pubblico?
Apri il tuo sito online in un browser, aggiungi /.env in fondo all’indirizzo e premi invio. Se torna una pagina della tua app, su quel percorso non viene servito niente. Se torna testo semplice, con righe come SUPABASE_URL=https://abcdefghij.supabase.co, il file viene consegnato a chiunque lo chieda. Fai lo stesso con /.env.local e /.env.production, perché un server che ne pubblica uno di solito li pubblica tutti e tre.
Un file .env dentro il mio bundle è la stessa cosa?
No, e il rimedio è diverso. Un valore che finisce dentro il tuo JavaScript ci è arrivato perché al build era stato chiesto, con una variabile chiamata VITE_ o NEXT_PUBLIC_ o EXPO_PUBLIC_. Il file non ha mai lasciato la tua macchina; il valore sì. Quello è un problema a parte con un suo articolo. Qui si parla del file in sé, servito dal tuo server web, cosa che rende pubblico ogni valore che contiene, con prefisso o senza.
Perché qualcuno può scaricare la mia cartella .git?
Perché il tuo server punta alla cartella del progetto e .git è una directory al suo interno come tutte le altre. Un server non sa che una di quelle è la tua cronologia delle versioni. Se /.git/config o /.git/HEAD risponde con del testo, di solito l’intera directory è leggibile, e questo comprende ogni commit che hai mai fatto, non solo il codice che hai oggi.
Ho trovato un backup.sql sul mio sito, e adesso?
Toglilo dalla cartella che il tuo server pubblica prima di fare qualunque altra cosa, perché il file viene consegnato mentre leggi queste righe. Poi tratta come pubblici ogni password, ogni chiave e ogni dato personale che contiene: un dump porta lo schema e tutte le righe di tutte le tabelle. Poi scopri come ci è finito, di solito qualcuno ha fatto una copia nella cartella del progetto e non l’ha mai spostata.
Ruoto le chiavi o cancello prima il file?
Sposta prima il file, poi ruota. Ruotare mentre il file viene ancora servito scrive i valori nuovi dentro qualcosa che chiunque può scaricare, quindi spendi il lavoro e torni al punto di partenza. Quando su quel percorso non risponde più nulla, ruota ogni segreto che il file conteneva, presso ogni fornitore, e poi guarda fatturazione e log.