Pular para o conteúdo

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.

Vlad Tkachenko9 min de leitura
Quatro pastas lacradas atrás de um muro e, na frente, uma pasta aberta com as linhas à mostra sob uma faixa âmbar.

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 aconteceuOnde está o valorO que custa resolver
Você chamou uma variável de VITE_, NEXT_PUBLIC_ ou EXPO_PUBLIC_Compilado no JavaScript que todo visitante baixaMover o trabalho para um servidor e rotacionar a chave. O arquivo nunca foi servido.
Seu servidor web publica a pasta do projetoNo arquivo, em https://seusite/.envTirar 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.

Esquerda: o build copiou um valor para o seu JavaScript, porque o prefixo mandou. Direita: o servidor entrega o arquivo, e os prefixos ali não mudam nada.

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çoO que guardaSe responder com texto
/.envToda chave com que seu aplicativo foi construídoTrate tudo como público e rotacione
/.env.localO mesmo, de uma execução localO mesmo
/.env.productionO mesmo, do seu deploy no arO mesmo
/.git/configO endereço remoto do seu repositórioO diretório .git inteiro costuma ser legível
/.git/HEADEm que branch você estáO mesmo, e é o mais silencioso dos doze
/.aws/credentialsChaves longas da AmazonRotacione na Amazon e depois confira a fatura
/database.sqlEsquema e linhasTodas as linhas de todas as tabelas são públicas
/dump.sqlO mesmoO mesmo
/backup.sqlO mesmoO mesmo
/config.jsonO que você colocou lá dentroLeia e veja; token aparece ali mais do que se imagina
/docker-compose.ymlDefinições de serviço, muitas vezes com senha dentroRotacione tudo que estiver escrito ali
/.npmrcUm token de registroRevogue 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.

Rotacione primeiro e a chave nova cai no arquivo que continua sendo entregue. Mova primeiro e não sobra nada para entregar.

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

Escrito por

Vlad Tkachenko

Fundador da Reeve

Passo meus dias olhando apps criados com Lovable, Bolt, v0, Cursor e Replit, e a curta lista de erros que aparecem neles sem parar.

Mais sobre o autor

Leia depois

Todos os artigos

Não sabe como está o seu próprio app?

Faça um scan gratuito e receba uma nota clara de A a F em cerca de 20 segundos. Sem conta e sem cartão.

Verificar meu app grátis

Verificação externa automatizada, não uma auditoria completa. A ausência de achados não é garantia de segurança.