Noções de segurança
Seu arquivo .env está exposto? Os doze caminhos para testar
Seu arquivo .env está exposto no seu próprio servidor web? Doze endereços dizem isso em um minuto, e um acerto significa que tudo nele já é público.

Em resumo
- Dois problemas diferentes têm o mesmo nome: um arquivo .env exposto. Este é o arquivo em si, parado no seu servidor web e baixável por qualquer um que digite o endereço.
- Um host que só serve seu frontend já construído não consegue fazer isso. Um servidor de verdade consegue, e por isso sete dos oito aplicativos que achamos eram do Replit.
- De 30.761 aplicativos no ar em que nossa verificação teve resposta, 8 estavam entregando um arquivo privado. Doze endereços dizem se você é o nono.
Alguém disse que seu arquivo .env está exposto, e pela frase você não consegue
saber se isso é sério. Ela cobre duas situações completamente diferentes. Uma
delas é a ferramenta funcionando como deve. A outra significa que um arquivo que
você achava privado pode ser baixado do seu site no ar, por qualquer um, desde o
dia em que está ali.
Esta é a parte que guia após guia mistura: um valor do seu .env acabar dentro
do seu JavaScript não é o mesmo acontecimento do arquivo .env em si sendo
servido pelo seu servidor web. O primeiro é o build fazendo o que você pediu
quando chamou a variável de VITE_ALGUMA_COISA. O segundo é um erro de
arquivamento, e é o que vale olhar primeiro, porque você mesmo consegue conferir
de fora em cerca de um minuto.
Pense no seu site no ar como o balcão de uma loja. Tudo que está no balcão está
ali para ser levado: a página, as imagens, o JavaScript, a logo. Os fundos
guardam o que faz a loja funcionar, e um visitante não tem caminho até lá. Um
valor compilado dentro do seu JavaScript é uma linha impressa no folheto do
balcão. O arquivo .env que responde em um endereço público é a pasta dos
fundos, deixada no balcão junto com o resto.
Meu arquivo .env está exposto?
Abra seu site no ar em um navegador, coloque /.env no fim do endereço e aperte
enter.
Três coisas podem voltar, e só uma delas é problema.
Uma página do seu aplicativo. A maioria dos frontends responde qualquer endereço desconhecido com a própria página índice, porque é assim que um aplicativo de página única faz o roteamento. Volta sua home, ou sua tela de não encontrado. Nesse caminho não está sendo servido nada.
Um erro. Um 403 ou um 404 significa que o servidor foi perguntado e recusou. É a resposta que você quer.
Texto puro, com linhas dentro. Algo como SUPABASE_URL=https://… e
SUPABASE_SERVICE_ROLE_KEY=eyJ…, num paredão monoespaçado sem estilo nenhum. O
arquivo está sendo entregue a quem pedir, e está assim desde o dia em que foi
servido pela primeira vez.
Duas coisas diferentes se chamam arquivo .env exposto
Uma é sobre seu bundle. A outra é sobre seu servidor.
| O que aconteceu | Onde está o valor | O que custa resolver |
|---|---|---|
Você chamou uma variável de VITE_, NEXT_PUBLIC_ ou EXPO_PUBLIC_ | Compilado no JavaScript que todo visitante baixa | Mover o trabalho para um servidor e rotacionar a chave. O arquivo nunca foi servido. |
| Seu servidor web publica a pasta do projeto | No arquivo, em https://seusite/.env | Tirar o arquivo do que o servidor publica e rotacionar tudo que estava nele. |
A primeira linha é a comum e tem um artigo próprio: o prefixo é uma instrução para publicar, e o build obedeceu. Tudo abaixo aqui é a segunda linha.
Por que um aplicativo Lovable não consegue e um Replit consegue
Porque os dois hosts publicam coisas diferentes.
Um builder que faz deploy de um frontend estático entrega ao host uma pasta de
arquivos construídos: um pouco de HTML, um pouco de JavaScript, algumas imagens.
Seu .env foi lido durante o build e não está nessa pasta, então em /.env não
há nada para ninguém pegar. O host não conseguiria servir o arquivo nem
querendo.
Um aplicativo Replit normalmente roda o próprio servidor, na própria pasta de projeto, com seus arquivos ao lado do seu código. Um servidor mandado servir a pasta dele serve todo arquivo que está ali, e não tem como saber que um deles guarda suas chaves. O mesmo vale para qualquer coisa que você subiu numa máquina sua.
Os números seguem a arquitetura. De 30.761 aplicativos no ar em que essa verificação teve resposta, 8 entregavam pelo menos um arquivo privado. Sete dos oito eram do Replit, e os do Replit eram 3.025 dos 30.761. O oitavo estava num domínio próprio, o que diz a mesma coisa de outro jeito: alguém rodava o próprio servidor.
É o achado mais raro que a gente imprime, e o único dos nove sem explicação inocente. Se você quer a leitura larga do que os aplicativos Replit realmente entregam, escaneamos 3.042 deles.
Os doze caminhos para testar
Doze endereços, na ordem que vale a pena. Coloque cada um depois do seu domínio.
| Endereço | O que guarda | Se responder com texto |
|---|---|---|
/.env | Toda chave com que seu aplicativo foi construído | Trate tudo como público e rotacione |
/.env.local | O mesmo, de uma execução local | O mesmo |
/.env.production | O mesmo, do seu deploy no ar | O mesmo |
/.git/config | O endereço remoto do seu repositório | O diretório .git inteiro costuma ser legível |
/.git/HEAD | Em que branch você está | O mesmo, e é o mais silencioso dos doze |
/.aws/credentials | Chaves longas da Amazon | Rotacione na Amazon e depois confira a fatura |
/database.sql | Esquema e linhas | Todas as linhas de todas as tabelas são públicas |
/dump.sql | O mesmo | O mesmo |
/backup.sql | O mesmo | O mesmo |
/config.json | O que você colocou lá dentro | Leia e veja; token aparece ali mais do que se imagina |
/docker-compose.yml | Definições de serviço, muitas vezes com senha dentro | Rotacione tudo que estiver escrito ali |
/.npmrc | Um token de registro | Revogue o token |
Nosso próprio escaneamento lê os doze de fora e nomeia os que responderam. Leva uns 20 segundos e não pede conta: escaneie seu aplicativo.
O que um acerto realmente significa
Tudo que está nesse arquivo é público agora, e é desde o dia em que ele foi servido pela primeira vez.
Você não vai descobrir quem leu. Um builder não te dá nenhum log de arquivo baixado que você possa ir olhar, e o pedido parece qualquer outro pedido por qualquer outro arquivo. Do que você pode ter certeza é que alguém tentou: há crawlers automáticos percorrendo exatamente esses doze caminhos pela internet toda, sem parar, e eles não precisam saber quem você é para achar o seu.
Cinco dos oito aplicativos que achamos entregavam um dos quatro que custam
caro na hora: .env, um diretório .git, .aws/credentials ou um dump .sql.
Os outros três entregavam config.json, docker-compose.yml ou .npmrc. Esses
três são os que todo mundo acha inofensivos, que é exatamente por que token e
senha de banco acabam escritos dentro deles.
O que isso não é, é um veredito sobre seu aplicativo. Uma verificação externa lê o que seu site entrega a um estranho, e doze caminhos que respondem com nada são doze caminhos que respondem com nada. O que mais escapa de um aplicativo vibe-coded é a lista longa.
O que estraga uma semana: um diretório .git
Um diretório .git não é um arquivo. É todo o seu histórico.
Cada commit está ali, incluindo aquele em que você colou uma chave e o seguinte
em que você tirou. É a parte que as pessoas entendem errado num vazamento de
.git: apagar um segredo do código que você tem hoje não faz nada com a versão
em que ele ainda estava, e essa versão está no mesmo diretório que seu servidor
publica.
A verificação lê /.git/config e /.git/HEAD porque os dois são pequenos, o
conteúdo deles não engana, e qualquer um dos dois respondendo significa que o
diretório em si está sendo servido. Dali em diante quem lê não precisa de
ferramenta nenhuma especial. O formato é documentado e software comum clona.
Se uma chave já esteve em um commit, rotacionar é a única coisa que ajuda. Qual vem primeiro depende de a cópia já estar fora, e essa ordem vale acertar.
O dump que alguém deixou na pasta
Três dos doze caminhos são dumps de banco: /database.sql, /dump.sql e
/backup.sql.
Um dump é cada linha de cada tabela em um arquivo, com o esquema em cima. Endereços de e-mail, senhas com hash, pedidos, mensagens, o que seu aplicativo guardar. Ele responde num endereço público pelo motivo mais sem graça deste artigo inteiro: alguém fez a coisa responsável, tirou uma cópia do banco e salvou na pasta de projeto em que estava.
Então o arquivo feito para proteger os dados virou o jeito mais rápido de ler todos eles. Onde uma cópia mora é tanto parte da decisão quanto tirar uma. Três jeitos de fazer backup de um banco Supabase passa pelas opções, e o Reeve Care guarda uma cópia do seu banco Supabase fora do seu próprio servidor, verificada antes de contar como backup.
Como corrigir, e por que a ordem importa
Tire o arquivo do que seu servidor publica. Depois rotacione cada segredo que estava nele. Nessa ordem.
Rotacionar primeiro parece a metade urgente, e é a metade que joga o trabalho fora. Suas chaves novas vão para o mesmo arquivo, o arquivo continua respondendo no mesmo endereço, e você rotacionou direto de volta para o vazamento. Nada está mais seguro do que dez minutos atrás.
Onde colocar ele, dependendo do que você roda:
- Um host com cofre de segredos próprio. Replit Secrets, as variáveis de ambiente de uma plataforma, o painel da sua hospedagem. O valor é lido pelo seu servidor em tempo de execução e nunca fica num arquivo dentro da pasta publicada.
- Fora da pasta servida. Se você controla a configuração do servidor, aponte ela para uma pasta de saída do build em vez da raiz do projeto. Aí um arquivo na raiz não tem endereço nenhum.
- No repositório também não, que é um hábito separado e bom. Para o problema de hoje ele não faz nada.
Esse último ponto vale dizer claro, porque .gitignore é a resposta que todo
mundo busca. Ele mantém o arquivo fora do seu repositório. O arquivo no seu
servidor chegou lá porque o servidor está sentado na sua pasta de projeto, e o
git não tem opinião sobre isso.
O que fazer agora
O que fazer
- Digite os doze endereços depois do seu próprio domínio, ou rode o escaneamento gratuito e deixe ele digitar. Leia o que volta, não o código de status.
- Se um responder com as suas configurações, tire o arquivo da pasta que seu servidor publica e faça o deploy de novo. É o passo que para isso.
- Depois rotacione cada chave, senha e token que o arquivo guardava, em cada provedor. Rotacionar antes de mover o arquivo coloca os valores novos onde estavam os velhos.
- Confira cobrança e logs do provedor no fim. Rotacionar para o que vem depois e não faz nada com o que já aconteceu.
- Se um diretório
.gitestava legível, rotacione tudo que já esteve em um commit, não só o que está no código hoje.
Se você prefere passar pelo seu aplicativo como uma lista, o checklist de segurança de 10 minutos cobre isso junto com as outras coisas que vale fechar num aplicativo recém-lançado.
Perguntas frequentes
Como sei se meu arquivo .env é público?
Abra seu site no ar em um navegador, coloque /.env no fim do endereço e aperte enter. Se voltar uma página do seu aplicativo, nada está sendo servido nesse caminho. Se voltar texto puro, com linhas como SUPABASE_URL=https://abcdefghij.supabase.co, o arquivo é entregue a quem pedir. Faça o mesmo com /.env.local e /.env.production, porque um servidor que publica um costuma publicar os três.
Um arquivo .env no meu bundle é a mesma coisa?
Não, e a solução é outra. Um valor que acaba dentro do seu JavaScript chegou lá porque foi pedido ao build, com uma variável chamada VITE_ ou NEXT_PUBLIC_ ou EXPO_PUBLIC_. O arquivo nunca saiu da sua máquina; o valor saiu. Esse é um problema separado e tem o próprio artigo. Este aqui é sobre o arquivo em si, servido pelo seu servidor web, o que deixa público cada valor que ele guarda, com prefixo ou sem.
Por que alguém consegue baixar minha pasta .git?
Porque seu servidor aponta para a pasta do projeto e .git é um diretório dentro dela como qualquer outro. Um servidor não sabe que um deles é seu histórico de versões. Se /.git/config ou /.git/HEAD responder com texto, o diretório inteiro costuma ser legível, e isso inclui cada commit que você já fez, não só o código que você tem hoje.
Achei um backup.sql no meu próprio site, e agora?
Tire ele da pasta que seu servidor publica antes de qualquer outra coisa, porque o arquivo está sendo entregue enquanto você lê isto. Depois trate como público cada senha, chave e dado pessoal que ele guarda: um dump leva o esquema e todas as linhas de todas as tabelas. Depois descubra como ele foi parar ali, normalmente alguém tirou uma cópia na pasta do projeto e nunca moveu.
Eu roto as chaves ou apago o arquivo primeiro?
Mova o arquivo primeiro, rotacione depois. Rotacionar enquanto o arquivo ainda está sendo servido escreve os valores novos em algo que qualquer um baixa, então você gasta o trabalho e termina onde começou. Quando nada mais responder nesse caminho, rotacione cada segredo que o arquivo guardava, em cada provedor, e confira cobrança e logs no fim.