Vulnerabilidade crítica no PHP: por que atualizar hoje
No dia 21 de outubro, o time do PHP publicou um aviso com um título que ninguém quer ler: "Critical remote code execution vulnerabilities, update now". São duas falhas. Juntas, elas permitem que alguém rode código no seu servidor sem fazer login. Essa vulnerabilidade crítica no PHP vale para quase toda instalação que atende requisição web. E no mod_php o ataque acontece antes de qualquer linha da sua aplicação rodar.
Talvez você esteja pensando: "Diego, meu stack é TypeScript e Bun, isso não é comigo". Calma. Eu também pensei isso por uns dois segundos. Depois lembrei do WordPress do cliente, do painel antigo que ninguém tem coragem de desligar e daquele VPS que roda um phpMyAdmin "só pra emergência".
O que aconteceu, em português claro
O comunicado oficial do PHP descreve duas falhas diferentes. As duas mexem com a forma como o PHP gerencia memória e estado entre uma requisição e outra.
CVE-2026-109618, corrupção por falta de memória. Quando o PHP fica sem memória no meio da montagem dos dados internos da requisição, ele para no meio do caminho. Depois, a rotina de limpeza olha para esses dados pela metade e trata tudo como se fosse válido. Dado inválido tratado como válido, dentro do runtime, é a receita clássica para execução de código.
CVE-2026-109619, estado global "velho". Um pedaço de estado interno, que pertence a uma requisição, continua vivo depois que a memória dele foi liberada. A próxima requisição atendida pelo mesmo worker pode usar esse estado antes da inicialização normal limpar tudo. É um use-after-free entre requisições.
Os detalhes técnicos completos estão nos advisories do GitHub (GHSA-w8hw-w9fx-j79w e GHSA-6wmf-4rv5-p3rp).
As versões corrigidas são estas:
- PHP 8.5.12
- PHP 8.4.27
- PHP 8.3.36
- PHP 8.2.35
Quem está exposto a essa vulnerabilidade crítica no PHP?
Resposta curta: quase todo mundo que serve PHP pela web. O aviso cita PHP-FPM, Apache com mod_php, php-cgi, FrankenPHP e LiteSpeed. O risco maior é no mod_php, onde já existe exploit demonstrado com uma requisição sem autenticação.
Esse detalhe do "antes de qualquer código da aplicação rodar" é o que mais pesa. Ele significa que seu framework não te salva. Seu middleware de autenticação não te salva. O WAF da aplicação, aquele if esperto na entrada do Laravel, nada disso chega a executar.
Quando a falha está no runtime, o código bonito da sua aplicação nem chega a ser consultado.
E tem o caso que mais me preocupa: PHP 8.1 e anteriores. O projeto não dá mais suporte a essas versões. Pela gravidade, o time de segurança publicou backports mínimos no código-fonte para 7.4, 8.0 e 8.1. Mas isso só ajuda quem compila do fonte. Se você instalou pelo gerenciador de pacotes de uma distro antiga, precisa ver se o seu fornecedor (distro ou vendor de LTS) fez o backport.
Como verificar e atualizar agora
Primeiro passo: descobrir o que você tem rodando. Parece óbvio, mas muita gente descobre servidor PHP esquecido justamente num dia como esse.
# versão do CLI (pode ser diferente da que atende web!)
php -v
# versão do FPM
php-fpm8.3 -v
# em container
docker exec meu-container php -v
Atenção ao comentário do primeiro comando. Já vi servidor com PHP 8.3 no CLI e um FPM 8.1 atendendo o site, porque alguém instalou as duas versões e esqueceu de uma. Confira o que atende requisição, não o que aparece no terminal.
Uma forma rápida de confirmar pela própria aplicação:
<?php
// coloque temporariamente, acesse e APAGUE em seguida
echo PHP_VERSION;
Depois, atualize pelo caminho que você usa:
- Distro Linux: as distribuições receberam os patches antes da divulgação. Rode o update normal do sistema e confira se a versão do pacote mudou.
- Docker: troque a tag da imagem para a versão corrigida, faça o rebuild e o redeploy. Se você usa
php:8.3-fpmsem fixar o patch, umdocker pullresolve. Mas só se você realmente puxar a imagem nova e recriar o container. - Hospedagem compartilhada: abra um chamado. Sério. Pergunte a versão exata.
E reinicie todos os workers. PHP-FPM com processo antigo vivo continua rodando código antigo, e a segunda falha mora justamente no estado que sobrevive entre requisições.
E se eu não puder atualizar hoje?
O próprio aviso é honesto aqui: só a atualização resolve de verdade. As medidas abaixo reduzem caminhos de ataque conhecidos, mas não deixam a instalação segura. Trate como curativo para ganhar algumas horas, não como solução.
Para a falha de memória (CVE-2026-109618):
; no pool do FPM ou na config do Apache, onde a aplicação não sobrescreve
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 6M
A lógica é tirar do atacante o controle sobre o momento em que o PHP estoura a memória. Se a aplicação precisa de upload, o aviso sugere post_max_size = 32M, sempre com upload_max_filesize abaixo disso. Também vale limitar no proxy o tamanho do corpo, o número de campos e de partes multipart.
Sim, memory_limit=-1 dá um frio na barriga. Por isso a recomendação é usar container ou cgroup como limite real, de preferência matando só o worker afetado. E um detalhe importante: não use RLIMIT_AS nem RLIMIT_DATA no lugar, porque eles podem causar exatamente a falha de alocação que leva ao bug.
Para a falha de estado global (CVE-2026-109619):
session.upload_progress.enabled = 0
; só no PHP 8.5:
fatal_error_backtraces = 0
Isso precisa estar no php.ini, no pool do FPM ou na config do servidor. Não adianta ini_set() nem .user.ini, porque o ataque acontece antes disso ser lido. Reinicie todos os workers depois.
Tem mais uma recomendação que achei interessante: esconder erro não ajuda. Usar @, display_errors=0 ou mexer no error_reporting não fecha nada. O que ajuda é remover configurações inválidas ou obsoletas aplicadas por requisição, que geram diagnóstico antes da inicialização, e corrigir fatal errors e timeouts que um atacante consiga provocar.
Por que isso importa para quem nem escreve PHP
O PHP ainda roda uma fatia enorme da web. Se você é dev full stack e atende cliente, a chance de ter um WordPress, um Magento ou um sistema legado no seu colo é alta. Muitas vezes nem foi você quem subiu. Mas é você que vai receber a ligação quando o site começar a redirecionar para cassino.
A lição de arquitetura também vale para o mundo Node e Bun. A segunda falha nasce do reuso de worker: o mesmo processo atende várias requisições e algo vaza de uma para outra. Quem escreve servidor em Node vive isso todo dia. Uma variável no escopo do módulo, um cache em memória sem chave por usuário, e pronto: dado de uma requisição aparece na outra. No PHP o bug está no runtime. No nosso caso, geralmente está no nosso código.
// o equivalente "caseiro" do bug, em qualquer servidor JS
let usuarioAtual: User | null = null // estado global compartilhado
app.get('/perfil', async (req) => {
usuarioAtual = await auth(req) // requisição concorrente pode sobrescrever
return render(usuarioAtual)
})
Se você tem algo assim, vale revisar hoje também.
Minha opinião
O time do PHP fez o trabalho certo. Avisou distros antes, publicou versões para todos os ramos suportados, fez backport para versões mortas e listou mitigação com a franqueza de dizer "isso não resolve". Pouca gente escreve aviso de segurança tão útil assim.
O problema real não é o PHP. É o servidor que ninguém lembra que existe. Minha regra é simples: se não tem dono, desliga. Se tem dono, atualiza essa semana, não no próximo sprint. Inventário de servidor é chato até o dia em que te salva.
Se você quiser ver como eu organizo deploy e atualização nos meus projetos, dá uma olhada em alguns dos projetos que mantenho.
Resumo para o LinkedIn
Seu stack pode ser 100% TypeScript e mesmo assim existir um PHP vulnerável com o seu nome no plantão. No dia 21 o PHP publicou duas falhas que permitem rodar código no servidor sem login. No mod_php o ataque acontece antes de qualquer linha da aplicação rodar. Ou seja: framework, middleware e autenticação nem chegam a ser consultados. Corrija hoje: 8.5.12, 8.4.27, 8.3.36 ou 8.2.35. Confira a versão que atende requisição, não a do terminal, e reinicie todos os workers. Minha regra para servidor esquecido é simples: se não tem dono, desliga. Se tem dono, atualiza essa semana. Escrevi o passo a passo completo, com as mitigações e o paralelo com Node e Bun. Quem tiver um WordPress antigo para cuidar, o link está nos comentários. #PHP #SegurançaDaInformação #DevOps #WebDevelopment #FullStack