Situação atual: PHP2Shell. Duas vulnerabilidades críticas do PHP (CVSS 9,2) permitem executar código malicioso sem autenticação. Patches disponíveis desde 21/10/2026.O que fazer agora →

Isolar pools PHP-FPM por site

Um pool próprio por site é o início, não o fim. Como verificar os pools existentes e reforçar sockets, extensões, páginas de estado, recursos e registos.

Atualizado em 6 min de leituraHardening e configuração

Em resumo: Cada site num servidor deve ter um pool PHP-FPM próprio, com utilizador de sistema próprio e socket próprio. A separação só se torna eficaz com os pormenores: permissões do socket apenas para o servidor web, security.limit_extensions em .php, nenhuma página de estado acessível publicamente, limites próprios para processos e tempo de execução e registos separados. Contra o PHP2Shell em si, só protege a atualização; os pools limitam até onde chega um ataque.

Um servidor, muitos sites, um risco partilhado

Em muitos servidores cresceu ao longo dos anos aquilo que ninguém planeou assim. O site da empresa, a loja, uma página de campanha de 2019 e um sistema de teste correm lado a lado, muitas vezes sob o mesmo utilizador e no mesmo pool. Se a página de campanha esquecida for comprometida, os dados dos clientes da loja ficam também expostos.

O PHP-FPM, o gestor de processos FastCGI do PHP, resolve isto com pools: grupos de processos worker com definições próprias. O princípio básico é explicado em PHP-FPM explicado: gestor de processos, pools e workers. A criação de pools separados, com utilizador próprio, pasta temporária própria e open_basedir, é descrita no guia open_basedir e isolamento por site. Este artigo dá um passo adicional: como verificar os pools existentes e que definições tornam a separação fiável?

Importante para o enquadramento: no PHP2Shell, as vulnerabilidades CVE-2026-109618 e CVE-2026-109619 estão no interpretador PHP, que todos os pools de uma versão do PHP partilham. Um pool próprio não impede a exploração. Mas decide se um atacante chega depois a um site ou a todos.

Pools PHP-FPM com utilizador próprio: verificar o existente

Antes de alterar seja o que for, obtenha uma visão geral. Os comandos seguintes apenas leem. Os caminhos e os números de versão variam consoante a distribuição:

# Todos os pools com utilizador e socket
grep -HE '^\s*(\[|user|group|listen)\s*' /etc/php/*/fpm/pool.d/*.conf

# Pools em execução e respetivos utilizadores
ps -eo user,pid,cmd | grep '[p]hp-fpm: pool'

# Sockets e respetivas permissões
ls -l /run/php/

# Verificar erros na configuração (não altera nada)
sudo php-fpm8.3 -t

Atenção a três padrões: vários sites no mesmo pool, pools que correm como www-data ou até como root, e sockets com permissão de escrita para todos os utilizadores. Qualquer um deles mina a separação.

Se no servidor correrem várias versões do PHP, cada uma tem o seu próprio serviço FPM, com diretórios de pools próprios. Verifique todas, não apenas a versão que configurou por último. Precisamente as versões antigas com pools esquecidos são um risco, porque muitas vezes não são mantidas nem reiniciadas após atualizações. Encontra indicações em Várias versões do PHP num servidor. Registe o resultado numa pequena tabela: site, pool, utilizador, socket, versão do PHP. Este quadro é, ao mesmo tempo, a base de qualquer alteração posterior.

As definições mais importantes por pool

O quadro seguinte baseia-se na configuração do FPM no manual do PHP. Os valores para processos e tempos dependem da sua carga e aplicação:

Definição Finalidade Recomendação
user, group utilizador dos workers utilizador próprio por site, nunca root
listen.owner, listen.group, listen.mode permissões do socket apenas o grupo do servidor web, modo 0660
listen.acl_users acesso por ACL em vez de proprietário registar apenas o utilizador do servidor web
security.limit_extensions extensões executáveis limitar a .php (por defeito .php .phar)
clear_env esvaziar variáveis de ambiente manter o valor por defeito yes
pm.max_children número máximo de workers limitar de modo que um site não esgote o servidor
request_terminate_timeout interrupção de pedidos bloqueados definir de acordo com o pedido legítimo mais longo
pm.status_path, ping.path página de estado e de ping definir só se for necessário e proteger internamente
slowlog, request_slowlog_timeout registo de pedidos lentos um ficheiro próprio por pool

Um excerto para um pool reforçado tem este aspeto:

; /etc/php/8.3/fpm/pool.d/shop.conf (excerto)
[shop]
user = shop
group = shop
listen = /run/php/shop.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
request_terminate_timeout = 60s

security.limit_extensions = .php
clear_env = yes

slowlog = /var/log/php/shop.slow.log
request_slowlog_timeout = 10s
php_admin_value[error_log] = /var/log/php/shop.error.log

Definições como memory_limit ou session.upload_progress.enabled fixam-se por pool com php_admin_value e php_admin_flag. Segundo o manual, estes valores não podem ser substituídos com ini_set(). Mais informação em php_admin_value: bloquear definições contra a sobrescrita.

Classificar corretamente as páginas de estado e a rotação de workers

pm.status_path e ping.path só estão ativos se estiverem definidos. A página de estado mostra pedidos em curso, processos e carga. Para a monitorização é útil, mas, se for acessível publicamente, é uma fonte de informação desnecessária para os atacantes. Encaminhe estes caminhos no servidor web apenas para endereços internos. Como o Nginx e o PHP-FPM colaboram em segurança é descrito em Proteger o Nginx com PHP-FPM.

pm.max_requests define após quantos pedidos um worker é reiniciado; o valor por defeito é 0, ou seja, nunca. Um valor superior a zero ajuda contra um consumo de memória que cresce lentamente. Como proteção contra o PHP2Shell não está pensado: o php.net não o menciona entre as medidas transitórias e sublinha que só a atualização protege por completo. Após a atualização, vale ainda: todos os workers de todos os pools têm de ser reiniciados, porque os processos antigos continuam, caso contrário, a correr com o código vulnerável.

Dica

Mantenha um modelo para novos pools e crie com ele cada novo site. Assim, a separação surge automaticamente, em vez de ser acrescentada anos mais tarde.

Erros típicos na migração

O erro mais frequente é um novo pool que corre sob um utilizador próprio, mas cujos ficheiros continuam a pertencer a www-data. Então o pool não pode escrever onde precisa e alguém define as permissões de forma demasiado generosa. Esclareça antes que pastas têm de permitir escrita. O segundo erro são as tarefas agendadas que continuam a correr sob o utilizador antigo e criam ficheiros com o proprietário errado. O terceiro é uma pasta de sessões partilhada por todos os pools, que volta a anular a separação.

Depois de cada migração, teste o início de sessão, os uploads, as caches e as tarefas agendadas do site e observe o registo de erros do pool.

Como ajuda concretamente a PHP SOS?

  • Registamos todos os pools, utilizadores, sockets e definições e mostramos onde os sites partilham hoje processos ou permissões.
  • Configuramos para cada site um pool reforçado, com permissões do socket, limite de extensões, limites de recursos e registos próprios.
  • Protegemos as páginas de estado, reiniciamos todos os workers de forma controlada e testamos cada site.
  • No âmbito do reforço de segurança de servidores, documentamos a separação como modelo para projetos futuros.

Perguntas frequentes

Porque deve cada site ter um pool PHP-FPM próprio?

Um pool próprio com utilizador de sistema próprio separa os sites ao nível dos processos e das permissões de ficheiros. Se um site for comprometido, o código não consegue, sem mais, ler os ficheiros dos outros. Além disso, os limites e as definições podem ser fixados separadamente para cada site.

Um pool próprio protege contra o PHP2Shell?

Não. As vulnerabilidades estão no próprio interpretador PHP, que todos os pools partilham. Só a atualização para uma versão corrigida, com reinício de todos os workers, protege. Pools separados limitam, no entanto, até onde chega um ataque bem-sucedido.

O que faz o security.limit_extensions?

Segundo o manual do PHP, a definição limita as extensões de ficheiro que o PHP-FPM executa como script principal. Por defeito são .php e .phar. Limitá-las a .php impede que, por exemplo, ficheiros de imagem carregados sejam executados como PHP, caso o servidor web esteja mal configurado.

A página de estado do PHP-FPM é perigosa?

Revela detalhes sobre processos e pedidos em curso. Se estiver acessível publicamente sem proteção, facilita a orientação dos atacantes. Segundo o manual, só está ativa se pm.status_path estiver definido. Nesse caso, o servidor web deve restringir o caminho a endereços internos.

Fontes

Contacto imediato

Cada hora conta. Escreva-nos agora.

Descreva brevemente o sistema, a versão do PHP e o que observou. Entramos em contacto consigo, esclarecemos a situação e trabalhamos remotamente até o incidente estar concluído e documentado.

Ajuda imediata