# open_basedir e isolamento por site

> Definir o open_basedir corretamente e separar sites: utilizador próprio, pool PHP-FPM próprio, pastas temporárias próprias. Com exemplo de configuração para Nginx.

Fonte: https://phpsos.de/pt/ratgeber/open-basedir/  
Atualizado em: 25/10/2026  
Prestador: PHP SOS, Assistência de emergência PHP e segurança de servidores. Ajuda imediata por WhatsApp: https://wa.me/491736494914?text=Ol%C3%A1%2C%20PHP%20SOS%2C%20precisamos%20de%20ajuda%20com%20o%20nosso%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**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](https://phpsos.de/pt/ratgeber/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.

```ini
; /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:

```bash
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](https://phpsos.de/pt/ratgeber/dateirechte-webserver/).

## 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.

```nginx
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](https://phpsos.de/pt/server/nginx-php-fpm/). Após o reinício, verifique se os pools correm realmente separados:

```bash
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.

> **Dica:**
> 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](https://phpsos.de/pt/branchen/agenturen-white-label/).

## 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](https://phpsos.de/pt/leistungen/server-haertung/).


## 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.

## Fontes

- [php.net: avisos de segurança](https://www.php.net/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [BSI IT-Grundschutz (proteção de base de TI)](https://www.bsi.bund.de/DE/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/IT-Grundschutz/it-grundschutz_node.html)
