Vai al contenuto

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.

Vlad Tkachenko9 min di lettura
Quattro cartelle sigillate dietro un muro e, davanti, una cartella aperta con le righe in vista sotto una fascia ambra.

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’è successoDov’è il valoreQuanto costa rimediare
Hai chiamato una variabile VITE_, NEXT_PUBLIC_ o EXPO_PUBLIC_Compilato nel JavaScript che scarica ogni visitatoreSpostare il lavoro su un server, poi ruotare la chiave. Il file non è mai stato servito.
Il tuo server web pubblica la cartella del progettoNel file, su https://tuosito/.envTogliere 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.

A sinistra: il build ha copiato un valore nel tuo JavaScript, perché il prefisso glielo chiedeva. A destra: il server consegna il file, e i prefissi lì non cambiano niente.

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.

IndirizzoCosa contieneSe risponde con del testo
/.envOgni chiave con cui la tua app è stata costruitaTratta tutto come pubblico e ruota
/.env.localLo stesso, da un’esecuzione localeLo stesso
/.env.productionLo stesso, dal tuo deploy onlineLo stesso
/.git/configL’indirizzo remoto del tuo repositoryDi solito tutta la directory .git è leggibile
/.git/HEADSu che branch seiLo stesso, ed è il più silenzioso dei dodici
/.aws/credentialsChiavi Amazon a lunga durataRuota su Amazon, poi guarda la fattura
/database.sqlSchema e righeTutte le righe di tutte le tabelle sono pubbliche
/dump.sqlLo stessoLo stesso
/backup.sqlLo stessoLo stesso
/config.jsonQuello che ci hai messo dentroLeggilo e guarda; i token ci stanno più spesso di quanto si pensi
/docker-compose.ymlDefinizioni di servizi, spesso con password dentroRuota tutto quello che ci è scritto
/.npmrcUn token di registroRevoca 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.

Ruota per primo e la chiave nuova atterra nel file che viene ancora consegnato. Sposta per primo e non resta niente da consegnare.

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 .git era 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.

Scritto da

Vlad Tkachenko

Fondatore di Reeve

Passo le giornate a guardare app costruite con Lovable, Bolt, v0, Cursor e Replit, e la breve lista di errori che vi ricompaiono di continuo.

Altro sull'autore

Da leggere dopo

Tutti gli articoli

Non sai come sta messa la tua app?

Esegui una scansione gratuita e ottieni un voto chiaro da A a F in una ventina di secondi. Senza account e senza carta.

Scansiona la tua app gratis

Controllo esterno automatizzato, non un audit completo. L'assenza di risultati non è una garanzia di sicurezza.