O que é directory listing e por que ele é perigoso
Directory listing (listagem de diretório) acontece quando um visitante acessa uma URL que aponta para uma pasta e o Apache, não encontrando um arquivo padrão (como index.html), responde com uma página listando os arquivos e subpastas daquele diretório. Isso pode expor nomes de arquivos sensíveis, versões, backups, dumps, endpoints internos e até artefatos de desenvolvimento.
O objetivo aqui é garantir que: (1) pastas não sejam listadas, (2) exista um arquivo padrão bem definido, (3) o DocumentRoot contenha apenas o que deve ser público, e (4) erros retornem respostas adequadas (403/404) com páginas personalizadas que não vazem detalhes.
Laboratório: reproduzir a listagem, desativar e verificar
1) Preparar um diretório “vulnerável” para o teste
Crie uma pasta dentro do seu site que não tenha arquivo de índice e coloque alguns arquivos para ficar evidente a listagem.
sudo mkdir -p /var/www/site/public/arquivos
echo "relatorio" | sudo tee /var/www/site/public/arquivos/relatorio.txt > /dev/null
echo "backup" | sudo tee /var/www/site/public/arquivos/site-backup.zip > /dev/nullAgora acesse no navegador: http://SEU_HOST/arquivos/. Se a listagem estiver habilitada para esse caminho, você verá os arquivos.
2) Confirmar no Apache se a listagem está habilitada
A listagem é controlada por Options Indexes (habilita) e Options -Indexes (desabilita). Em muitos ambientes, a listagem aparece quando Indexes está ativo em algum contexto aplicável (por exemplo, em um <Directory> ou configuração do VirtualHost).
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
Para o laboratório, aplique a correção diretamente no bloco do diretório público do seu site (no VirtualHost correspondente), garantindo que o diretório e subdiretórios não listem conteúdo.
3) Desativar directory listing com Options -Indexes
No arquivo do VirtualHost do site, ajuste o bloco do diretório do conteúdo público (exemplo genérico):
<Directory /var/www/site/public>
Options -Indexes
</Directory>Recarregue o Apache:
sudo apachectl configtest
sudo systemctl reload apache2Verifique novamente http://SEU_HOST/arquivos/. Agora a listagem deve desaparecer. Dependendo da presença (ou não) de um arquivo padrão, você verá um erro (geralmente 403) ou será redirecionado para um índice se existir.
4) Verificar via linha de comando (útil em servidores sem navegador)
curl -i http://SEU_HOST/arquivos/Procure por HTTP/1.1 403 Forbidden (ou outro código) e confirme que não há HTML com lista de arquivos.
Definir arquivos padrão com DirectoryIndex (sem depender de listagem)
Mesmo com -Indexes, é importante garantir que diretórios “navegáveis” tenham um arquivo padrão. Isso evita que o usuário caia em erro ao acessar uma pasta que deveria ter uma página inicial.
Boas práticas para DirectoryIndex
- Defina explicitamente quais arquivos são aceitos como índice.
- Evite listas longas e desnecessárias (reduz tentativas e comportamentos inesperados).
- Garanta que o arquivo exista nos diretórios que devem ser acessíveis diretamente.
Exemplo de configuração no VirtualHost:
DirectoryIndex index.html index.phpExemplo prático: crie um índice para o diretório do laboratório e teste novamente:
echo "<h1>Área de arquivos</h1>" | sudo tee /var/www/site/public/arquivos/index.html > /dev/nullAgora http://SEU_HOST/arquivos/ deve abrir o index.html em vez de erro.
Organização segura do DocumentRoot: não exponha artefatos
Problema comum: DocumentRoot apontando para a raiz do projeto
Um erro frequente é apontar o DocumentRoot para a pasta do projeto inteiro (onde ficam código-fonte, arquivos de build, backups, dumps e metadados). Mesmo com -Indexes, arquivos podem ser acessados diretamente se o atacante souber (ou adivinhar) o caminho, por exemplo: /backup.sql, /.env, /.git/, /dump.tar.gz.
Estrutura recomendada: separar “public” do restante
Organize o projeto com uma pasta pública dedicada (somente arquivos que devem ser servidos):
/var/www/site/
├── public/ (DocumentRoot)
│ ├── index.html
│ ├── assets/
│ └── uploads/ (se necessário, com regras específicas)
├── app/ (código, não público)
├── config/ (não público)
├── storage/ (não público)
├── backups/ (não público)
└── dumps/ (não público)O DocumentRoot deve apontar para /var/www/site/public, nunca para /var/www/site.
Checklist do que NÃO deve ficar dentro do DocumentRoot
- Pastas de controle de versão:
.git/,.svn/. - Backups:
*.bak,*.old,*.zip,*.tar.gz. - Dumps e exports:
*.sql,*.dump. - Arquivos de ambiente/segredos:
.env, chaves, tokens, credenciais. - Arquivos de build e CI: pipelines, manifests internos, relatórios.
Camada extra: bloquear acesso a artefatos caso apareçam no DocumentRoot
Mesmo com organização correta, é útil adicionar uma “rede de segurança” para negar acesso a padrões perigosos. Isso reduz impacto de erros humanos (por exemplo, alguém copia um backup para dentro de public/).
Negar acesso a diretórios e arquivos sensíveis
Exemplo de regras no VirtualHost (ou em um arquivo incluído), aplicadas ao diretório público:
<Directory /var/www/site/public>
Options -Indexes
# Bloqueia diretórios de VCS e arquivos ocultos (dotfiles) comuns
<FilesMatch "(?i)^\.">
Require all denied
</FilesMatch>
# Bloqueia padrões comuns de backup/dump
<FilesMatch "(?i)\.(bak|old|zip|tar|gz|tgz|sql|dump)$">
Require all denied
</FilesMatch>
</Directory>Observações importantes:
<FilesMatch>atua sobre nomes de arquivos; ele não substitui a necessidade de tirar artefatos doDocumentRoot.- Bloquear “dotfiles” ajuda a evitar exposição de arquivos como
.enve também caminhos como/.git/(por conter arquivos iniciando com ponto). Ainda assim, a melhor prática é não ter essas pastas no conteúdo público.
Retornar respostas adequadas: quando usar 403 vs 404
403 Forbidden
Use quando o recurso existe, mas você quer negar acesso (por política). Exemplos: bloquear .git, bloquear .sql, bloquear .env. O Apache normalmente retornará 403 quando uma regra de acesso negar.
404 Not Found
Use quando o recurso não existe (ou quando você quer “parecer que não existe”). Para reduzir sinalização, muitas equipes preferem responder 404 para certos padrões sensíveis, mas isso exige cuidado: você não deve mascarar erros a ponto de dificultar operação e troubleshooting.
Como forçar 404 para padrões sensíveis (opcional)
Uma abordagem simples é redirecionar para um handler de 404 usando RedirectMatch ou regras de reescrita. Exemplo com RedirectMatch (aplicado no VirtualHost):
# Responde como "not found" para tentativas óbvias em .git e backups
RedirectMatch 404 (?i)^/\.git(/|$)
RedirectMatch 404 (?i)^/.*\.(bak|old|zip|tar|gz|tgz|sql|dump)$Valide com:
curl -i http://SEU_HOST/.git/
curl -i http://SEU_HOST/arquivos/site-backup.zipVocê deve ver HTTP/1.1 404 Not Found (se essas regras estiverem antes de outras que capturem a rota).
Páginas de erro personalizadas sem vazar detalhes
Páginas padrão de erro podem revelar informações (layout do servidor, mensagens genéricas que incentivam enumeração, ou até páginas de aplicação com stack trace se mal integradas). O objetivo é: (1) manter mensagens simples, (2) não exibir caminhos internos, versões, nomes de módulos, variáveis, e (3) ainda permitir rastreio via logs.
1) Criar páginas estáticas de erro dentro do conteúdo público
Crie uma pasta dedicada para erros e páginas simples:
sudo mkdir -p /var/www/site/public/errors
cat | sudo tee /var/www/site/public/errors/404.html > /dev/null <<'EOF'
<!doctype html>
<html lang="pt-br"><meta charset="utf-8">
<title>Página não encontrada</title>
<h1>Página não encontrada</h1>
<p>Verifique o endereço e tente novamente.</p>
EOF
cat | sudo tee /var/www/site/public/errors/403.html > /dev/null <<'EOF'
<!doctype html>
<html lang="pt-br"><meta charset="utf-8">
<title>Acesso negado</title>
<h1>Acesso negado</h1>
<p>Você não tem permissão para acessar este recurso.</p>
EOFEssas páginas são propositalmente simples e não exibem detalhes técnicos.
2) Configurar ErrorDocument no VirtualHost
No VirtualHost do site:
ErrorDocument 403 /errors/403.html
ErrorDocument 404 /errors/404.htmlRecarregue o Apache:
sudo apachectl configtest
sudo systemctl reload apache23) Testar os erros sem vazar informações
- Teste 404:
curl -i http://SEU_HOST/nao-existe - Teste 403: tente acessar um arquivo bloqueado por regra (por exemplo, um
.sqldentro depublic/apenas para o teste) e verifique se a resposta usa sua página personalizada.
Verifique também o error.log para garantir que o diagnóstico fica no servidor (logs), não na resposta ao cliente.
Mini-roteiro de verificação (checklist rápido)
| Item | Como verificar | Resultado esperado |
|---|---|---|
| Directory listing desativado | curl -i http://SEU_HOST/arquivos/ sem index | Sem lista de arquivos; 403 ou página de índice |
| DirectoryIndex definido | Adicionar index.html e acessar a pasta | Abre o índice |
DocumentRoot aponta para public/ | Revisar VirtualHost | Nada sensível fora de public/ é servível |
| Bloqueio de dotfiles e backups | curl -i http://SEU_HOST/.env e arquivos de teste | 403 ou 404 conforme sua política |
| ErrorDocument sem vazamento | Testar 403/404 | Páginas simples, sem caminhos internos |