Em resumo: Protege um ficheiro .env fazendo com que ele nem sequer esteja no diretório acessível ao público: o webroot aponta para uma subpasta como public/, e a configuração fica um nível acima. Além disso, o servidor web bloqueia todos os ficheiros com ponto no início, o ficheiro só é legível pelo proprietário e pelo processo PHP e nunca pertence ao repositório Git. Após uma intrusão, todas as credenciais nele contidas são consideradas conhecidas e substituídas.
O ficheiro com as chaves da casa
Os projetos PHP modernos guardam de bom grado os seus segredos num ficheiro chamado .env: palavra-passe da base de dados, acesso ao servidor de correio, chaves de API para serviços de pagamento e de expedição, a chave com que a aplicação assina cookies. Frameworks como o Laravel e o Symfony usam este padrão, e muitos desenvolvimentos próprios adotaram-no.
A ideia é boa: configuração e código ficam separados, e cada ambiente recebe os seus valores. O perigo surge quando o ficheiro vai parar ao diretório errado. Se estiver no webroot e o servidor web não o bloquear, basta uma única chamada no navegador para ler todos os acessos de uma vez.
A isso juntam-se variantes com outros nomes: .env.local, .env.production, config.local.php ou ficheiros com credenciais de serviços de armazenamento. O que aqui se descreve para .env vale para todos os ficheiros onde existem segredos.
Estes ficheiros são procurados continuamente por scanners automatizados. Não é uma especialidade de atacantes individuais, mas ruído de fundo da Internet, semelhante às varreduras em massa à procura de versões PHP sem patch.
Onde deve estar o ficheiro .env
A medida mais eficaz é estrutural: o servidor web só deve servir a pasta que realmente deve ser pública. No Laravel e no Symfony, é a pasta public/. Todo o resto, ou seja, código, configuração, registos e .env, fica acima dela e nem sequer é endereçável por HTTP.
/srv/www/shop/
├── .env ← inacessível
├── config/
├── src/
├── vendor/
└── public/ ← único webroot
├── index.php
└── assets/
Na prática, o webroot aponta muitas vezes para o próprio diretório do projeto, por exemplo porque um painel de alojamento o impõe ou porque, numa mudança, um caminho não foi ajustado. Verifique por isso, na configuração do servidor web ou no painel, para onde aponta root ou DocumentRoot.
Proteger o ficheiro .env: o bloqueio no servidor web como segunda linha
Se o webroot não puder ser alterado, ou se quiser uma proteção adicional, bloqueie os ficheiros ocultos no servidor web. A pasta .well-known tem de continuar acessível, pois é usada, entre outras coisas, para a emissão de certificados.
# Bloquear todos os ficheiros e pastas com ponto no início, exceto .well-known
location ~ /\.(?!well-known/) {
deny all;
return 404;
}
# Bloquear ficheiros com ponto no início
<FilesMatch "^\.">
Require all denied
</FilesMatch>
Uma resposta 404 em vez de 403 não revela que o ficheiro existe. Se o bloqueio funciona, verifica-se a partir do exterior:
# Esperado: 403 ou 404, nunca 200
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env.example
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env.bak
As duas últimas linhas não são por acaso: cópias como .env.bak ou .env.old surgem durante trabalhos de manutenção e são muitas vezes esquecidas. Mais sobre estes vestígios em Ficheiros de backup no webroot.
Permissões, Git e o tratamento de cópias
Também fora do webroot vale o princípio das permissões mínimas. Uma divisão provada:
- O proprietário é o utilizador de deploy, não o processo PHP.
- O grupo é o do pool PHP-FPM, que só pode ler; permissões
640. - Sem acesso de escrita para o processo PHP. Uma aplicação não precisa de poder alterar as suas próprias credenciais.
- Sem valores reais no repositório: só o
.env.examplecom marcadores de posição é submetido; o ficheiro real consta do.gitignore. - Valores separados por ambiente: desenvolvimento, staging e produção usam acessos diferentes, para que uma fuga num ambiente de teste não abra a produção.
chown deploy:shop-php /srv/www/shop/.env
chmod 640 /srv/www/shop/.env
# Verificar se o ficheiro alguma vez foi submetido
git log --all --oneline -- .env
Atenção também às páginas de erro. Em modo de depuração, os frameworks mostram por vezes variáveis de ambiente e valores de configuração; num sistema de produção, este modo tem de estar desligado. Verifique-o após cada deployment, pois um valor errado no próprio .env pode voltar a ligá-lo.
Se o último comando devolver resultados, as credenciais estão no histórico do Git e consideram-se expostas, mesmo que o ficheiro hoje já não exista. Porque é que isso é particularmente delicado quando a pasta .git está no servidor é descrito em Pasta .git pública: uma fuga de dados subestimada. Os princípios sobre permissões são explicados no guia Permissões de ficheiros no servidor web.
Os ficheiros de teste com phpinfo() mostram variáveis de ambiente. Quem passa segredos como variáveis de ambiente revela-os através de um ficheiro de teste esquecido. Os antecedentes estão em Ficheiros phpinfo(): porque têm de desaparecer.
Após uma intrusão: substituir as credenciais
Nenhum bloqueio protege contra um atacante que já executa código no servidor. Um processo PHP tem de poder ler a sua configuração, pelo que o código introduzido no mesmo processo também o pode. Após o PHP2Shell aplica-se, por isso: se não se puder excluir uma intrusão, todas as credenciais no servidor têm de ser tratadas como conhecidas.
Uma substituição ordenada abrange palavras-passe de bases de dados, acessos de correio e SMTP, chaves de API de serviços externos, chaves de assinatura da aplicação e acessos a serviços de armazenamento. A ordem e o momento devem ser planeados: quem substitui primeiro e só depois limpa o servidor pode entregar de imediato os novos valores ao atacante.
O dano de uma fuga limita-se já antes, fazendo com que cada segredo possa apenas o necessário. O utilizador da base de dados do site não precisa de direitos sobre outras bases de dados. As chaves de API de serviços externos podem, em muitos fornecedores, ser restringidas a funções individuais ou a endereços de remetente, por exemplo criar envios, mas não ordenar pagamentos. E cada aplicação recebe acessos próprios, em vez de vários sites partilharem a mesma conta de correio ou a mesma chave. Assim, uma fuga afeta uma área claramente delimitada, e a substituição permanece gerível.
Em ambientes de contentores, o ficheiro .env não pertence nem à imagem nem a um registry acessível ao público. Exclua-o através do .dockerignore e passe os segredos só em tempo de execução, pelos mecanismos da plataforma.
A longo prazo vale a pena olhar para repositórios de segredos centralizados, como a OWASP os recomenda: segredos num só local, com registo de acessos, substituição regular e validade o mais curta possível.
Como ajuda concretamente a PHP SOS?
- Verificamos a partir do exterior se ficheiros de configuração, cópias e ficheiros ocultos dos seus sites estão acessíveis.
- Reorganizamos o webroot, os bloqueios do servidor web e as permissões de ficheiros de modo a que os segredos já não sejam acessíveis por HTTP.
- Verificamos repositórios e deployments à procura de credenciais submetidas.
- Após um incidente, planeamos e acompanhamos a substituição de todos os acessos afetados, na ordem certa.
Estes trabalhos fazem parte do nosso reforço de segurança de servidores para servidores web PHP.
Perguntas frequentes
Um ficheiro .env no webroot é automaticamente público?
Não automaticamente, mas muitas vezes. Se pode ser acedido depende da configuração do servidor web. Muitas configurações por defeito protegem apenas os ficheiros .htaccess, não outros ficheiros com ponto no início. Um teste com curl esclarece a situação.
As variáveis de ambiente são mais seguras do que um ficheiro .env?
Não por princípio. A OWASP adverte que as variáveis de ambiente são legíveis por todos os processos do utilizador e podem aparecer em registos ou em despejos de memória. Evitam, no entanto, um ficheiro no webroot. Melhores são os repositórios de segredos centralizados, onde o ambiente os suporta.
O que fazer se o ficheiro .env esteve acessível?
Considerar todas as credenciais nele contidas como conhecidas e substituí-las: base de dados, correio, APIs, serviços de pagamento, chaves da aplicação. Depois, verificar nos registos de acesso se o ficheiro foi consultado e, no caso de dados pessoais, envolver o encarregado da proteção de dados.
O ficheiro .env pertence ao repositório Git?
Não. No repositório fica, no máximo, um modelo sem valores reais, por exemplo .env.example. O ficheiro real é excluído através do .gitignore e disponibilizado separadamente no servidor.
O phpinfo() pode revelar credenciais?
Sim. O phpinfo() mostra, entre outras coisas, variáveis de ambiente e variáveis do servidor. Se aí estiverem segredos, são apresentados. Os ficheiros de teste com phpinfo() nunca devem, por isso, estar num sistema acessível.