Em resumo: Quando um navegador pede uma página PHP, o servidor web aceita o pedido e passa-o ao PHP através de uma interface (SAPI). O PHP traduz o script para bytecode, executa-o na Zend Engine e devolve o resultado, geralmente HTML, ao servidor web. Depois, o estado do pedido é limpo, para que o seguinte comece em condições limpas.
O que acontece quando alguém acede ao seu site?
Para os visitantes, é um clique. No servidor decorrem muitos passos em milissegundos. Quem conhece este percurso compreende melhor por que razão as atualizações, os reinícios e a configuração são tão importantes e por que razão uma falha no próprio PHP pesa mais do que uma falha num plugin.
O manual do PHP refere três componentes necessários para um site PHP: o interpretador PHP (como CGI ou módulo do servidor), um servidor web e um navegador. O interpretador é o programa que lê e executa o código PHP. A forma como está ligado ao servidor web determina o modo de operação.
Como funciona o PHP? Sete passos de um pedido
- Pedido: O navegador envia ao servidor um pedido HTTP, por exemplo
GET /kontakt.php. - Servidor web: Apache, Nginx ou LiteSpeed aceitam o pedido. Entregam eles próprios os ficheiros estáticos, como imagens. Para ficheiros
.php, chamam o PHP. - Passagem através da SAPI: A interface do servidor (SAPI) passa ao PHP os dados do pedido, como cabeçalhos, parâmetros e dados de formulários. No PHP-FPM isto faz-se através do protocolo FastCGI; no mod_php, diretamente no processo do Apache. Pormenores em SAPI explicada.
- Preparar o pedido: O PHP guarda os dados do pedido em variáveis como
$_GET,$_POST,$_COOKIEe$_FILESe reserva memória para o pedido. - Traduzir: O script é decomposto em elementos (tokens), daí resulta uma árvore sintática abstrata (AST) e dela os opcodes, uma espécie de linguagem máquina para o PHP. Se o OPcache estiver ativo, este passo é ignorado quando o script já se encontra na cache.
- Executar: A máquina virtual da Zend Engine processa os opcodes, consulta bases de dados, lê ficheiros e gera a saída.
- Resposta e limpeza: A saída regressa ao servidor web através da SAPI e daí ao navegador. Em seguida, o PHP liberta a memória do pedido e repõe o estado.
A AST existe desde o PHP 7. Segundo o RFC correspondente, antes disso o analisador gerava os opcodes diretamente durante a leitura; a separação em três fases tornou o compilador mais fácil de manter.
Um exemplo: o que o script vê do pedido
O seguinte script inofensivo mostra que informações o PHP recebe do pedido e em que modo de operação corre:
<?php
header('Content-Type: text/plain; charset=utf-8');
echo 'Betriebsart (SAPI): ' . PHP_SAPI . "\n";
echo 'PHP-Version: ' . PHP_VERSION . "\n";
echo 'Methode: ' . ($_SERVER['REQUEST_METHOD'] ?? '-') . "\n";
echo 'Speicher in dieser Anfrage: ' . memory_get_usage() . " Bytes\n";
Num servidor com PHP-FPM, PHP_SAPI devolve o valor fpm-fcgi; no Apache com mod_php, apache2handler. Guarde estes ficheiros de teste apenas por pouco tempo e nunca de forma permanente no diretório público, pois as indicações de versão também ajudam os atacantes a orientar-se.
Onde entram a cache de bytecode e o JIT
A tradução no passo 5 consome tempo. Segundo o manual, o OPcache guarda bytecode pré-compilado na memória partilhada e poupa assim ao PHP o carregamento e a decomposição dos scripts a cada pedido. Desde o PHP 8.0, um compilador JIT pode ainda gerar código máquina real a partir dos opcodes. Segundo o RFC do JIT, é uma parte quase autónoma do OPcache.
| Fase | Resultado | É guardado em cache? |
|---|---|---|
| Análise léxica | fluxo de tokens | não |
| Análise sintática | árvore sintática abstrata (AST) | não |
| Compilação | opcodes (op-arrays por ficheiro e função) | sim, no OPcache |
| JIT (opcional) | código máquina | sim, na memória do OPcache |
| Execução | saída da página | não |
A cache diz respeito apenas ao código traduzido. Os dados de cada pedido, como entradas de formulários ou valores de sessão, não são partilhados através do OPcache.
Porque é que o PHP2Shell atua precisamente neste percurso
As duas vulnerabilidades do PHP2Shell não dizem respeito a uma aplicação, mas ao próprio percurso. No CVE-2026-109618, uma condição de falta de memória pode interromper o PHP enquanto os dados do pedido estão só parcialmente preparados, ou seja, no passo 4. Na limpeza que se segue, dados inválidos podem então ser tratados como válidos.
No CVE-2026-109619, o estado interno de um pedido pode persistir depois de a memória ser libertada. Um pedido posterior no mesmo worker pode usá-lo antes de ser reposto. Isto diz respeito ao passo 7, a limpeza. Os antecedentes estão na página CVE-2026-109619 e no artigo sobre o ciclo de vida do pedido.
Daqui resulta: as medidas de proteção no código da aplicação, como os plugins de segurança, só atuam no passo 6. No mod_php, segundo a equipa do PHP, chegou a ser demonstrado um exploit antes de qualquer código de aplicação correr. Só é eficaz a atualização do PHP para 8.5.12, 8.4.27, 8.3.36 ou 8.2.35 e o reinício de todos os processos PHP, porque, de outro modo, os workers de longa duração continuam a executar o código antigo.
Como ajuda concretamente a PHP SOS?
- Tornar o percurso visível: Apuramos que modo de operação, que configuração e que cache estão realmente ativos no seu servidor.
- Atualizar corretamente: Aplicamos a atualização do PHP e verificamos, através das horas de arranque, que já não corre nenhum processo antigo.
- Proteger as camadas: No âmbito do reforço de servidores, limitamos tamanhos de pedidos, memória e permissões onde fazem efeito: antes do código da aplicação.
- Verificar vestígios: Em servidores acessíveis sem proteção, procuramos sinais de intrusão.
Perguntas frequentes
O código PHP é traduzido de novo a cada acesso?
Sem cache, sim: o PHP decompõe o script, constrói uma árvore sintática e gera a partir dela os opcodes. Com o OPcache ativo, os opcodes ficam na memória partilhada e são reutilizados enquanto o ficheiro não for alterado.
O que fica na memória depois de um pedido?
Em princípio, o estado de um pedido é limpo no fim, para que o pedido seguinte comece em condições limpas. Só coisas como o OPcache ou as ligações persistentes têm vida longa. É precisamente nesta limpeza que atua o CVE-2026-109619.
Porque é que o PHP tem de ser reiniciado depois de uma atualização?
No PHP-FPM e no mod_php, os processos correm de forma permanente e têm carregado o código de programa antigo. Novos ficheiros no disco não alteram isso. Só um reinício carrega o código corrigido.
Um servidor web sem PHP é mais seguro?
Tem menos superfície de ataque, porque nenhuma lógica de programa reage aos pedidos. Para conteúdos puramente estáticos, é uma opção real. Para lojas, formulários e autenticações, porém, é preciso uma linguagem do lado do servidor, e então conta a sua manutenção.