Pular para conteúdo

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 off os 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.