Para: qualquer pessoa que rode vários sites em hospedagem compartilhada/alugada ou em um VPS, fazendo deploy em diretórios públicos (public/, public_html/, htdocs/ etc.). Nenhum passo de ataque — só auditar o seu próprio servidor e fechar os buracos. Para proteger o próprio .env, veja protegendo o .env em hospedagem compartilhada.
A visão deste site: onde você coloca É o controle de acesso
Segredos vazam mais por acidentes de posicionamento do que por ataques engenhosos. O webroot é "um lugar de onde qualquer um pode vir baixar pela URL." Coloque um segredo ali e até a senha de servidor mais forte fica sem sentido. A regra básica deste site é manter segredos fora tanto do webroot quanto do git (→ guardando segredos com segurança). A ideia é simples: coloque no diretório público só coisas que você não se importa que qualquer um veja.
public/ = baixável por qualquer um via URL
OK colocar
imagens, CSS, JS — assets públicos
Nunca colocar
.env, token.json, .sql, .git, .bak
Acima da raiz do app = não servido
Coloque aqui
segredos, tokens, credenciais, backups. Permissão 600 (leitura só do dono). Inalcançável por URL.
Por que "resquícios no diretório público" acontecem
O padrão comum é um arquivo colocado por conveniência, ou um que veio dentro de um boilerplate, ficando silenciosamente público.
Por exemplo, um JSON guardando um token de acesso para uma integração de terceiros fica sob public/ por muito tempo — e se for de um template compartilhado, o mesmo arquivo é copiado para o mesmo ponto em muitos sites, então todos compartilham o mesmo problema. Corrigir um site não adianta nada se os outros ainda têm o arquivo. Igualmente perigosos: .env esquecidos, backups de banco de dados (.sql), um diretório .git, backups de editor (*.bak) e atalhos (relacionado: path traversal · um .env inteiro exposto).
Arquivos que ficam para trás, e por que cada um é perigoso
"Arquivo de segredo" costuma lembrar o .env, mas o que de fato fica para trás é bem mais amplo. O guia de testes da OWASP (uma organização sem fins lucrativos focada em segurança web) também lista versões antigas de arquivos, backups e arquivos sem nenhum link apontando para eles como itens a verificar.
| Tipo | Nomes típicos | O que está em risco | Onde deveria ficar |
|---|---|---|---|
| Arquivos de configuração | .env, config.php.bak, settings.json | Senhas de banco de dados e chaves de API escritas em texto puro | Acima da raiz do app (não servido) |
| Tokens e credenciais | token.json, credentials.json, *.pem, *.key | Login direto num serviço externo ou em outro servidor | Fora do webroot, permissão 600 |
| Backups manuais ou do editor | login.php.old, index.php~, .index.php.swp, *.orig | Com a extensão trocada, o arquivo pode não ser executado como código, então o código-fonte aparece como texto puro | Não os crie em produção; edite localmente |
| Dumps de banco de dados | backup.sql, dump.sql.gz | Dados pessoais dos usuários e hashes de senha, tudo num arquivo só | Um local dedicado a backups, de preferência fora do servidor |
| Arquivos compactados do site | site.zip, www.tar.gz | Contêm o .env e o código-fonte completo | Apague logo depois de extrair |
| Dados de controle de versão | .git/ | Segredos de commits antigos podem ser recuperáveis | Fora do diretório público |
| Valores embutidos no build | dentro de assets/index-xxxx.js | Variáveis que começam com VITE_ ou NEXT_PUBLIC_ acabam no JavaScript do navegador | Mantenha segredos só em variáveis do lado do servidor |
A última linha é fácil de deixar passar. Mesmo que você nunca tenha colocado um arquivo ali, um build pode empurrar um segredo para o diretório público. A documentação oficial do Vite diz claramente que variáveis VITE_ não devem conter informações sensíveis, como chaves de API.
Audite o seu próprio servidor
Primeiro, revele mecanicamente "quaisquer arquivos com cara de segredo no meu próprio diretório público." Isto é inspecionar os seus próprios ativos, não espiar o site de outra pessoa.
Procure sob os diretórios públicos por arquivos com cara de segredo
public/ (JSON de token/credenciais, .env, chaves, atalhos).Presuma que o mesmo arquivo existe em outros lugares — verifique todo host e site
Decida por arquivo se ele realmente precisa ser público
# audit under your own public directories (inspecting your own assets)
find ~ -path '*/public/*' \( -iname '*token*.json' -o -iname '*credential*.json' \
-o -iname '*.bak' -o -iname '*.sql' -o -iname '.env*' \) 2>/dev/null
# an exposed .git directory is dangerous too
find ~ -path '*/public/*/.git' -maxdepth 8 2>/dev/nullComo verificar o seu próprio ambiente
O find acima é um ponto de partida. Percorrer as cinco verificações abaixo pega resquícios que uma busca só por nome de arquivo deixa passar. Rode-as apenas no seu próprio servidor e no seu próprio domínio. As verificações específicas do .env (olhar os primeiros bytes da resposta, confirmar que o .htaccess funciona de fato) estão em protegendo o .env em hospedagem compartilhada.
Procure também backups e arquivos compactados
O nome do diretório público varia conforme a hospedagem, então inclua public_html, htdocs e www. Por exemplo, rode find ~/public_html \( -name '*~' -o -name '*.swp' -o -name '*.old' -o -name '*.orig' -o -name '*.zip' -o -name '*.tar.gz' -o -name '*.pem' -o -name '*.key' \) 2>/dev/null e confirme, arquivo por arquivo, que cada resultado deve mesmo ser público.
Confirme que a listagem de diretórios está desativada
Abra no navegador um diretório que não tem index.html (por exemplo /uploads/). Se você vir uma lista de nomes de arquivos, o recurso de listagem de diretórios do servidor web está ativado. Uma listagem permite que as pessoas encontrem resquícios sem saber os nomes. Um 403 ou 404 é o que você quer.
Procure segredos no JavaScript gerado pelo build
Se você faz build de um frontend, pesquise a pasta de saída que vai para o deploy (dist/, .next/static/ e similares). Use algo como grep -rlE 'BEGIN [A-Z ]*PRIVATE KEY|secret|password' dist/, mais o prefixo que o seu serviço usa nas chaves. Se encontrar algo, verifique se a variável tem um nome voltado ao navegador (VITE_, NEXT_PUBLIC_).
Confira se os segredos fora do webroot não são legíveis por outros usuários
Tirar um segredo do webroot não adianta se permissões frouxas deixam outra conta do mesmo servidor lê-lo. find ~ -maxdepth 3 \( -name '.env*' -o -name '*.pem' -o -name '*token*.json' \) -perm /o+r 2>/dev/null lista segredos legíveis por qualquer um além do dono; corrija com chmod 600. Sem saída significa que está tudo certo.
Verifique se os buscadores indexaram algo
Pesquise no Google site:seudominio e procure arquivos que você não reconhece (.sql, .zip, .txt e outros). Se você usa o Search Console, o relatório Páginas lista as URLs indexadas. O que fazer se algo ainda está nos resultados de busca está em erros comuns, abaixo.
O que fazer quando você acha um
Faça três coisas como um conjunto — remover, revogar, mover. Pule qualquer uma e a correção fica incompleta.
Remova do webroot para que a URL não consiga baixá-lo
Revogue/reemita qualquer chave ou token que possa ter vazado
Mantenha segredos fora do webroot com permissão 600
Por servidor: configurações que se recusam a servir esses arquivos (como reforço)
A correção real é não colocar os arquivos ali. Além disso, acrescente uma regra de bloqueio no servidor web para que algo colocado por engano ainda assim não seja servido. O que dá para usar depende da sua configuração.
| Configuração | Como bloquear | Listagem de diretórios | Atenção |
|---|---|---|---|
| Apache (VPS) | <FilesMatch> + Require all denied num bloco <Directory> ou no .htaccess | Desative com Options -Indexes | No Apache 2.4 o AllowOverride tem padrão None, então o .htaccess é ignorado a menos que você o permita |
| Nginx (VPS) | deny all; dentro de um location | autoindex é off por padrão | Não lê .htaccess. Blocos location com regex são verificados na ordem em que foram escritos, e o primeiro que casar vence |
| Hospedagem compartilhada | Normalmente .htaccess (compatível com Apache) | Pelo painel de controle ou pelo .htaccess | Confirme que funciona com um arquivo de teste que não contenha segredos |
| Hospedagem estática / CDN | O que estiver na pasta de saída é publicado como está | Depende do serviço | Conte com manter segredos fora da pasta de saída, não com regras de bloqueio |
Exemplo para Apache (onde o .htaccess está habilitado):
Options -Indexes
<FilesMatch "(^\.|~$|\.(bak|old|orig|swp|sql|zip|tar|gz|pem|key)$)">
Require all denied
</FilesMatch>Exemplo para Nginx (dentro de um bloco server):
# Allow the path used for automatic certificate renewal (Let's Encrypt) first
location ^~ /.well-known/acme-challenge/ { }
# Deny files and directories starting with a dot (.env, .git, ...)
location ~ /\. { deny all; }
# Deny backups and archives
location ~* (~|\.(bak|old|orig|swp|sql|zip|tar|gz|pem|key))$ { deny all; }No Nginx, quando uma requisição casa com um location marcado com ^~, os locations com regex não são verificados. Assim, /.well-known/acme-challenge/, usado na renovação de certificados, continua sendo servido apesar da regra que bloqueia caminhos iniciados por ponto. Depois de mudar a configuração, confira a sintaxe com nginx -t, recarregue e confirme que um arquivo de teste retorna 403. Se o seu site oferece arquivos ZIP ou similares para download, tire essa extensão da lista de bloqueio.
O posicionamento em que as pessoas caem (perigoso)
- um token JSON para uma integração fica em
public/ - um arquivo de segredo que veio no boilerplate permanece público
- backups (
.sql/.bak) ou.gitdeixados sob o diretório público - corrigir um site e considerar "resolvido"
O posicionamento correto
- o diretório público contém só coisas que podem ser públicas
- segredos ficam fora do webroot com permissão 600
- extensões sensíveis têm o serviço negado
- ache um e audite todo host e site
Faça de 'só coisas seguras para o público' uma regra permanente
Regras vencem correções pontuais. Decida que o diretório público contém só arquivos seguros para o público e, por regra, nunca coloque ali segredos, backups, dados de controle de versão (.git) ou arquivos de configuração. Ao atualizar um boilerplate, retire o segredo do próprio template — senão todo site que você produzir em massa a seguir continua copiando o mesmo arquivo de segredo.
Erros comuns e como corrigi-los
- Confiar numa regra de bloqueio e deixar o arquivo no lugar: uma mudança de servidor ou uma reescrita da configuração pode derrubar a regra, e o arquivo fica público a partir desse dia. O Nginx nem lê o
.htaccess. Trate a regra como reforço e tire o arquivo do diretório público. - Editar arquivos direto no servidor de produção: os editores deixam para trás backups terminados em
~ou.swp. O guia de testes da OWASP também desaconselha editar arquivos no próprio servidor web. Edite localmente e envie as mudanças pelo seu deploy. - Copiar a pasta inteira do projeto para o diretório público:
.env,.gite dados de teste vão junto. Sirva só a pasta de saída do build e defina exatamente o que é copiado. - Bloquear todo caminho com ponto e quebrar a renovação de certificados: o Let's Encrypt coloca o arquivo de verificação em
/.well-known/acme-challenge/. Como no exemplo de Nginx acima, libere esse caminho primeiro. - Colocar segredos em variáveis
VITE_ouNEXT_PUBLIC_: elas são embutidas no JavaScript do build para qualquer um ler. Use-as só no código do lado do servidor e revogue qualquer chave que já tenha sido publicada assim. - Apagar o arquivo e parar por aí: se um buscador o indexou, ele pode continuar nos resultados. Confirme que a URL retorna 404 ou 410 e depois oculte-a temporariamente (cerca de seis meses) com a ferramenta Remoções do Google Search Console, enquanto um novo rastreamento a remove.
Um caso real: dados pessoais ocultos num relatório publicado
O Department of Health Care Finance (DHCF), que administra o Medicaid (cobertura pública de saúde para pessoas de baixa renda) em Washington, D.C., disse que dois relatórios estatísticos publicados no seu site de 2023 até julho de 2026 continham os dados pessoais de base como "campos ocultos". Os relatórios foram feitos para mostrar só números agregados, mas os arquivos ainda guardavam os números do Medicaid dos beneficiários, datas de nascimento e mais. A notificação ao Departamento de Saúde e Serviços Humanos dos EUA (HHS) informou 399.086 pessoas afetadas. O DHCF soube do problema em 21 de julho de 2026 e removeu os relatórios.
Não é um caso de arquivo de segredo esquecido, mas a raiz é a mesma:
- Um arquivo num diretório público pode ser baixado por inteiro, não só a parte mostrada na tela
- A informação que quem publica acha que "não está visível" é a mais fácil de passar despercebida numa revisão
- Ela pode ficar publicada por anos antes que alguém perceba
Uma etapa que confere "o que de fato há dentro deste arquivo" antes de publicar evita tanto arquivos esquecidos quanto dados ocultos. O relato completo está em a exposição de dados do Medicaid em Washington, D.C. (em inglês).
O que este site faz por conta própria
A regra básica deste site é manter segredos — chaves, tokens, strings de conexão — fora tanto do diretório público quanto do repositório de código. Os artefatos de deploy contêm apenas assets públicos construídos; os segredos ficam como variáveis de ambiente do ambiente de execução, em um lugar que não é servido. O motivo: a classe de incidente deste artigo é o caso típico em que um único arquivo esquecido vaza tudo para qualquer um que solicite sua URL. Projetamos o posicionamento em si como controle de acesso e rodamos esta auditoria na mesma cadência do inventário de resposta a vulnerabilidades.
Leia a seguir
- Stacks: protegendo o .env em hospedagem compartilhada · o playbook de resposta a vulnerabilidades (inventário)
- Glossário: arquivos .env e segredos · path traversal
- Incidente: um .env inteiro exposto
- Armazenamento: guardando segredos com segurança
- Na nuvem: buckets públicos (em inglês; o mesmo problema, fora do servidor)
- Quando o provedor é invadido: o que fazer quando sua hospedagem é comprometida (em inglês; presuma que o que estava lá foi lido)
- Caso real: números do Medicaid deixados em relatórios publicados (Washington, D.C.) (em inglês; um arquivo publicado exposto por inteiro)
Fontes
- IPA, "20 Checkpoints for Secure Website Operation and Management" (em japonês; arquivos que não devem ser públicos, cache de buscadores): ipa.go.jp
- OWASP, "Web Security Testing Guide: Review Old Backup and Unreferenced Files for Sensitive Information" (backups .old, ~, .swp; não editar no próprio servidor): wstg.owasp.org
- Apache HTTP Server 2.4, "Upgrading to 2.4 from 2.2" (
AllowOverridetem padrãoNone) e "mod_autoindex" (Options +Indexesgera listagens): httpd.apache.org, httpd.apache.org - nginx, "ngx_http_core_module: location" (ordem de correspondência e
^~) e "ngx_http_autoindex_module" (autoindextem padrãooff): nginx.org, nginx.org - Let's Encrypt, "Challenge Types" (o HTTP-01 usa
/.well-known/acme-challenge/): letsencrypt.org - Vite, "Env Variables and Modes" (variáveis
VITE_ficam expostas ao código do cliente): vite.dev - Next.js, "Environment Variables" (variáveis
NEXT_PUBLIC_são embutidas no JavaScript do navegador): nextjs.org - Ajuda do Google Search Console, "Removals tool" (a remoção temporária dura cerca de seis meses): support.google.com
- District of Columbia DHCF, "Notice of Data Incident": dhcf.dc.gov; HIPAA Journal (número informado ao HHS): hipaajournal.com