Em resumo: o open_basedir limita as pastas a que o PHP pode aceder. Sozinho, é apenas uma barreira adicional. Um isolamento eficaz por site só surge na combinação: utilizador de sistema próprio, pool PHP-FPM próprio, pasta temporária própria e acessos próprios à base de dados. Assim, uma intrusão num site fica limitada a esse site.
Porque é que o isolamento decide o dano após o PHP2Shell
Em muitos servidores correm vários sites lado a lado: o site da empresa, uma loja, um site de campanha antigo, e nas agências muitas vezes dezenas de projetos de clientes. Frequentemente partilham um utilizador de sistema e um processo PHP. Se um deles for comprometido, todos os outros ficam expostos.
O PHP2Shell torna este risco palpável. As vulnerabilidades CVE-2026-109618 e CVE-2026-109619 permitem a execução de código malicioso sem autenticação, independentemente do CMS. Segundo notícias dos meios de comunicação, já a 17/10/2026 houve uma primeira tentativa de ataque; os patches seguiram-se a 21/10. A correção é a atualização. Mas se um ataque no período anterior atinge um ou dez sites, isso decide-o o isolamento.
Sobretudo na segunda vulnerabilidade, acresce outra ideia: segundo o projeto PHP, o estado interno de um pedido pode persistir num worker e ser usado por um pedido posterior no mesmo worker. Pools separados por site garantem que os sites, de qualquer modo, não partilham workers.
O que o open_basedir consegue
Com o open_basedir define uma lista de pastas. As funções de ficheiros do PHP, como fopen, include ou file_get_contents, recusam então o acesso a tudo o que estiver fora dessa lista. Um script introduzido não consegue assim, sem mais, ler os ficheiros de configuração do site vizinho.
Os limites são importantes: a verificação ocorre dentro do PHP. Os programas iniciados fora do PHP não estão sujeitos a ela, e erros no interpretador podem contorná-la. Por isso, o open_basedir deve sempre andar junto com funções de sistema bloqueadas, como descrito no guia Usar corretamente o disable_functions, e sobretudo com separação ao nível do sistema operativo.
Isolamento por site em quatro camadas
Uma separação sólida assenta em várias camadas. Cada uma apanha o que a anterior não consegue.
| Camada | Medida | Efeito |
|---|---|---|
| Sistema operativo | utilizador de sistema próprio por site | os ficheiros de outros sites não são legíveis |
| Processos PHP | pool PHP-FPM próprio com socket próprio | nenhum worker partilhado, definições próprias |
| Configuração PHP | open_basedir, pasta tmp e de sessões própria |
barreira adicional, sem ficheiros temporários partilhados |
| Dados | utilizador de base de dados próprio, com permissões apenas na base de dados própria | sem acesso a dados de clientes alheios |
Com elevada necessidade de proteção, por exemplo em lojas com processamento de pagamentos ou portais com dados de saúde, acrescentam-se contentores ou servidores separados.
Configurar o open_basedir no pool PHP-FPM
O exemplo seguinte mostra um pool para um site com o utilizador próprio seite1. Adapte os caminhos e o número de versão ao seu sistema.
; /etc/php/8.3/fpm/pool.d/seite1.conf
[seite1]
user = seite1
group = seite1
listen = /run/php/seite1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
php_admin_value[open_basedir] = /var/www/seite1/:/var/www/seite1-tmp/
php_admin_value[upload_tmp_dir] = /var/www/seite1-tmp/
php_admin_value[sys_temp_dir] = /var/www/seite1-tmp/
php_admin_value[session.save_path] = /var/www/seite1-tmp/sessions
php_admin_value[error_log] = /var/log/php/seite1.error.log
php_admin_flag[session.upload_progress.enabled] = off
A última linha aplica logo a medida transitória do php.net para o PHP2Shell. Só produz efeito a partir do pool, do php.ini ou da configuração do servidor, não através de ini_set() nem de .user.ini. Crie as pastas com as permissões adequadas:
sudo useradd --system --home /var/www/seite1 --shell /usr/sbin/nologin seite1
sudo mkdir -p /var/www/seite1-tmp/sessions
sudo chown -R seite1:seite1 /var/www/seite1-tmp
sudo chmod 700 /var/www/seite1-tmp
Como devem ser as permissões na própria pasta web, descreve-o o guia Permissões de ficheiros no servidor web.
Ligar o servidor web e verificar o resultado
No Nginx, cada site aponta para o seu próprio socket. Assim, nenhum pedido vai parar ao pool de outro site.
server {
server_name seite1.example;
root /var/www/seite1/public;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/seite1.sock;
}
}
Mais indicações sobre esta combinação encontra-as em Proteger o Nginx com PHP-FPM. Após o reinício, verifique se os pools correm realmente separados:
sudo systemctl restart php8.3-fpm
# Cada pool deve correr com o seu próprio utilizador
ps -eo user,pid,cmd | grep "[p]hp-fpm: pool"
Teste depois todas as funções do site: uploads, tratamento de imagens, caches, tarefas agendadas. Mensagens de erro com a indicação open_basedir restriction no registo de erros mostram que caminho ainda falta. Acrescente apenas caminhos de que a aplicação realmente precise, e nunca o sistema de ficheiros inteiro.
As agências com muitos sites de clientes devem manter a configuração do pool como modelo. Assim, cada novo site recebe a separação automaticamente, em vez de ter de a acrescentar mais tarde. Antecedentes para este caso estão em Parceiro de segurança para agências.
Obstáculos típicos na migração
A migração para pools separados é planeável, mas tem algumas armadilhas recorrentes. A mais frequente diz respeito a bibliotecas partilhadas: se, por exemplo, uma framework ou uma pasta de tipos de letra estiver fora da pasta do site, esse caminho tem de ser incluído no open_basedir apenas para leitura. É melhor trazer essas dependências para o próprio projeto.
A segunda armadilha são as sessões. Se o local de armazenamento mudar, todos os visitantes ficam uma vez sem sessão e os carrinhos de compras em curso perdem-se. Planeie por isso a migração numa janela tranquila e anuncie-a internamente.
Em terceiro lugar, as tarefas agendadas correm muitas vezes pela linha de comandos e, portanto, com um utilizador diferente do site. Verifique as tarefas cron e as filas de espera e faça-as correr com o novo utilizador do site; caso contrário surgem ficheiros com proprietário errado, que o PHP depois já não pode alterar.
Por fim, cada pool deve ter limites próprios de processos e de memória. Um site que fique sob carga ou seja atacado não consegue assim deitar abaixo os outros.
Como ajuda concretamente a PHP SOS?
- Verificamos que sites partilham hoje utilizadores, processos e bases de dados.
- Configuramos para cada site utilizadores, pools, pastas tmp e acessos à base de dados próprios, sem alterar as aplicações.
- Testamos cada site após a migração e corrigimos de forma orientada os caminhos em falta.
- Documentamos a separação como modelo para projetos futuros.
Como isto se integra num conceito global, descreve-o o serviço Reforço de segurança de servidores PHP.
Perguntas frequentes
O open_basedir basta para separar sites uns dos outros?
Não. O open_basedir é uma barreira adicional dentro do PHP, não uma separação rígida. O isolamento só se torna fiável com utilizadores de sistema e pools próprios por site e, com maior necessidade de proteção, com contentores ou servidores separados.
Porque é que os uploads deixam de funcionar depois de definir o open_basedir?
Na maioria dos casos falta na lista a pasta dos ficheiros temporários. Crie para cada site uma pasta tmp própria, inclua-a no open_basedir e defina upload_tmp_dir e sys_temp_dir para essa pasta.
Uma aplicação pode anular o open_basedir por si própria?
Durante a execução, uma aplicação só pode restringir ainda mais a definição, não a pode relaxar. Ainda assim, defina o open_basedir através de php_admin_value no pool, para que o valor fique inequivocamente fixado.
O isolamento também é possível em alojamento partilhado?
Isso depende do fornecedor. Muitas ofertas separam os clientes através de utilizadores e processos PHP próprios. Pergunte como a separação é feita, sobretudo se vários dos seus sites estiverem num só pacote.