phpguard: Proteja PHP não corrigido contra ataques multipart php2shell
Requer o plano Pro (ou superior) da assinatura GetPageSpeed NGINX Extras.
Instalação
Você pode instalar este módulo em qualquer distribuição baseada em RHEL, incluindo, mas não se limitando a:
- RedHat Enterprise Linux 7, 8, 9 e 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 e Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-phpguard
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-phpguard
Ative o módulo adicionando o seguinte no topo de /etc/nginx/nginx.conf:
load_module modules/ngx_http_phpguard_module.so;
Este documento descreve o nginx-module-phpguard v1.0.0 lançado em 12 de novembro de 2026.
Proteja PHP não corrigido contra php2shell (CVE-2026-109618, CVE-2026-109619 e CVE-2026-115560) na camada do NGINX, sem tocar no PHP.
O php2shell é alcançado através do próprio parser multipart/form-data do PHP. Esse parser é executado antes de qualquer código da aplicação e antes de qualquer login, portanto todo endpoint PHP está exposto. Regras simples do NGINX só conseguem bloquear todas as requisições multipart, o que quebra uploads, e deixam passar as codificações que o PHP aceita.
O phpguard lê cada corpo multipart no NGINX antes que o PHP veja um único byte,
e o verifica com um parser de streaming que espelha o do próprio PHP. Ele foi
verificado em PHP 5.6, 7.4, 8.1, 8.4 e 8.5, atrás do PHP-FPM e atrás do Apache
mod_php através de proxy_pass.
O que ele bloqueia
- O campo de progresso de upload de sessão, o gatilho confirmado do php2shell, em todas as grafias que o PHP aceita: com ou sem aspas, qualquer caixa, cabeçalhos dobrados, linhas apenas com LF, content types com vírgula, corpos chunked e HTTP/2.
- Partes contrabandeadas. Apenas multipart canônico é aceito. Um preâmbulo, um epílogo, NUL ou bytes de controle, aspas ambíguas, ou uma string de boundary em qualquer lugar exceto um delimitador real é recusado. Isso inclui a parte que o PHP encontra após 5120 bytes quando ele pula um upload que não mantém.
- Parses com estouro de memória. A memória de parse de cada requisição no PHP é estimada
com um modelo de custo medido em todas as versões suportadas do PHP, e a
requisição é recusada acima de
phpguard_max_memory. Inputs de arquivo vazios com nomes profundamente aninhados, por exemplo, podem de outra forma fazer o PHP alocar mais de 100 MB para alguns kilobytes de input.
Apenas requisições multipart são inspecionadas. GET, JSON e formulários codificados em URL custam apenas uma verificação de cabeçalho.
Configuração
Ative-o para o servidor inteiro, para que o corpo seja verificado onde quer que seja lido primeiro:
server {
phpguard block;
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm.sock;
include fastcgi_params;
}
}
Comece com phpguard detect; para registrar o que seria recusado sem
bloquear nada.
Diretivas
| Diretiva | Padrão | Contexto | Descrição |
|---|---|---|---|
phpguard off \| detect \| block |
off |
http, server, location | block (alias on) recusa; detect apenas registra |
phpguard_status <code> |
403 |
http, server, location | Status para recusas de política (400-599). Corpos malformados sempre recebem 400 |
phpguard_progress_name <name> |
PHP_SESSION_UPLOAD_PROGRESS |
http, server, location | Nome de campo a recusar; repita para vários. Defina-o se session.upload_progress.name for alterado |
phpguard_max_memory <size> |
32m |
http, server, location | Memória de parse estimada do PHP por requisição. Mantenha-a bem abaixo do seu menor memory_limit |
phpguard_max_parts <n> |
1000 |
http, server, location | Partes por corpo |
phpguard_max_name_length <n> |
512 |
http, server, location | Bytes em um nome de campo |
phpguard_max_nesting <n> |
32 |
http, server, location | Níveis de [ em um nome de campo |
A variável $phpguard contém o motivo da recusa (vazia quando a requisição
passou), por exemplo em um log_format.
Notas
- As barras de progresso de upload do PHP (
session.upload_progress) param de funcionar, porque seu campo é exatamente o que é recusado. - Corpos multipart são totalmente armazenados em buffer antes que o PHP os receba. Esse é
o padrão do NGINX; com
fastcgi_request_buffering offos uploads param de fazer streaming para o PHP. - Um nome de campo ou de arquivo que termina em uma barra invertida é recusado. O PHP faz o parse incorreto de tais nomes de qualquer forma.
- O phpguard cobre os caminhos multipart transportados por requisição do php2shell. Ele não corrige o PHP: erros de estouro de memória que sua aplicação alcança através de outros inputs, e futuros bugs do PHP fora do parser multipart, permanecem. Atualize o PHP quando puder.