In breve: con Nginx e PHP-FPM la vulnerabilità di PHP2Shell si trova in PHP-FPM, non in Nginx. Aggiorni tutte le versioni di PHP-FPM, riavvii per intero ogni servizio FPM e lo dimostri con gli orari di avvio dei processi. Per la sicurezza di Nginx e PHP-FPM nel lungo periodo contano pool separati, valori non sovrascrivibili e una configurazione di Nginx che non esegua i file caricati.
Come collaborano Nginx e PHP-FPM
Nginx serve da sé i file statici e inoltra le richieste agli script PHP a PHP-FPM tramite FastCGI. PHP-FPM è composto da un processo master e da più processi worker, raggruppati in pool. Un worker elabora una dopo l'altra molte richieste.
È proprio questo a essere rilevante per PHP2Shell. Con CVE-2026-109619, lo stato interno di una richiesta può persistere dopo il rilascio della memoria ed essere usato da una richiesta successiva nello stesso worker. Con CVE-2026-109618, una condizione di out-of-memory può interrompere PHP nel mezzo dell'inizializzazione di una richiesta. Entrambe le vulnerabilità sono valutate CVSS v4.0 9.2 e sono corrette in PHP 8.5.12, 8.4.27, 8.3.36 e 8.2.35.
Verificare l'inventario: versioni, pool, processi
Molti server hanno in parallelo più versioni di PHP-FPM. I comandi seguenti leggono soltanto; i nomi di pacchetti e servizi sono esempi per Debian/Ubuntu e la famiglia RHEL.
# Quali servizi FPM sono in esecuzione?
systemctl list-units --type=service | grep -i fpm
# Quale versione c'è dietro? (esempio Debian/Ubuntu)
php-fpm8.3 -v
# Famiglia RHEL
php-fpm -v
# A quali socket o porte inoltra Nginx?
nginx -T 2>/dev/null | grep -E 'fastcgi_pass'
# Pool e relativi utenti
grep -RhE '^\[|^user|^listen' /etc/php/*/fpm/pool.d/ 2>/dev/null
Confronti ogni voce fastcgi_pass con un pool in esecuzione. Così vede quale versione di PHP serve quale sito. Per i pacchetti delle distribuzioni è il changelog con i numeri CVE a provare la patch, non il solo numero di versione.
Un riscontro frequente in questi inventari sono i server su cui nel corso degli anni sono state installate più versioni di PHP, delle quali una sola è effettivamente in uso. Gli altri servizi FPM continuano comunque a girare, spesso con pool standard in ascolto su un socket o una porta locale. Dall'esterno di solito non sono raggiungibili direttamente, ma restano un rischio inutile e allungano ogni aggiornamento d'emergenza. Disattivi e rimuova le versioni che nessun sito utilizza, dopo aver verificato l'assegnazione tramite le voci fastcgi_pass.
Vale la pena guardare anche all'utente dei pool. Se tutti i pool girano come www-data o nginx, la separazione tra i siti esiste solo sulla carta. Chi prende il controllo di un sito può allora leggere i file di tutti gli altri, ad esempio file di configurazione con password del database. Utenti di sistema separati per ogni pool e permessi dei file adeguati creano qui un vero confine. Non è un rimedio per PHP2Shell, ma limita in modo sensibile il possibile danno di un attacco riuscito.
Installare la patch e rinnovare tutti i worker
Dopo l'aggiornamento dei pacchetti riavvii ogni servizio PHP-FPM. Un reload sostituisce sì i worker, ma solo un riavvio completo rinnova anche il processo master ed è la via sicura dopo gli aggiornamenti di sicurezza.
# Esempio Debian/Ubuntu, per ogni versione installata
systemctl restart php8.3-fpm
# Famiglia RHEL
systemctl restart php-fpm
# Controllo: orario di avvio di master e worker
ps -eo pid,lstart,cmd | grep -E 'php-fpm' | grep -v grep
Ogni processo deve essere stato avviato dopo l'aggiornamento. La procedura è descritta in dettaglio alla pagina Riavviare PHP-FPM e tutti i worker.
Una patch non rimuove il malware introdotto prima. Conservi i log di accesso e di errore di Nginx a partire dal 20/10/2026 e verifichi la presenza di tracce nei siti.
Impostare i valori transitori nel pool
Finché una versione non è aggiornata, php.net raccomanda misure transitorie. Non offrono una protezione completa. In PHP-FPM imposti i valori con php_admin_value o php_admin_flag nel pool: in questo modo le applicazioni non possono sovrascriverli con ini_set().
; Esempio: /etc/php/8.3/fpm/pool.d/website.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 24M
Senza funzione di upload, php.net indica post_max_size = 8M. Su PHP 8.5 si aggiunge fatal_error_backtraces=0. Per l'indicazione su memory_limit=-1 serve un limite di memoria a livello di cgroup, ad esempio tramite MemoryMax in un'integrazione systemd per il servizio FPM. In Nginx adegui client_max_body_size al nuovo post_max_size, in modo che gli utenti ricevano messaggi di errore comprensibili. Poi riavvii FPM.
Migliorare nel tempo la sicurezza di Nginx e PHP-FPM
Oltre al caso acuto aiutano alcune regole di base che limitano il danno in caso di future vulnerabilità:
- Un pool per ogni sito con utente di sistema proprio, socket proprio e confine
open_basedirproprio. Maggiori informazioni alla pagina open_basedir e isolamento per sito. - Permessi del socket impostati in modo che possa accedervi solo Nginx; non aprire all'esterno porte FPM.
- Eseguire solo script esistenti: nella location PHP impostare
try_files $uri =404;, in modo che Nginx non passi a PHP percorsi inesistenti. - Non eseguire gli upload: vietare l'esecuzione di PHP nelle directory di upload. Trova indicazioni alla pagina Implementare in sicurezza gli upload di file.
- Proteggere le pagine di stato: rendere
pm.status_patheping.pathraggiungibili solo dall'interno.
# Esempio: nessuna esecuzione di PHP nella directory di upload
location ~* ^/uploads/.*\.php$ {
deny all;
}
Verifichi ogni modifica con nginx -t prima di ricaricare Nginx. Annoti inoltre quali pool, versioni e regole particolari esistono sul server. Questa breve documentazione fa risparmiare tempo prezioso alla prossima vulnerabilità critica, perché nessuno deve prima cercare quale sito dipenda da quale pool.
Come aiuta concretamente PHP SOS?
- Assegniamo ogni sito al proprio pool e alla propria versione di PHP e verifichiamo l'effettivo stato dei pacchetti.
- Aggiorniamo, riavviamo tutti i servizi FPM e documentiamo gli orari di avvio dei processi come prova.
- Fino alla patch impostiamo i valori transitori per ogni pool e limitiamo la memoria tramite cgroup.
- Nell'ambito dell'hardening dei server separiamo in modo pulito pool, permessi e directory di upload.
- In caso di sospetta intrusione ci occupiamo dell'incident response.
Domande frequenti
Dopo un aggiornamento basta systemctl reload per PHP-FPM?
Un reload rinnova i worker, ma il processo master può mantenere il vecchio codice. Dopo un aggiornamento di sicurezza la via sicura è un riavvio completo del servizio. Poi controlli l'orario di avvio di master e worker.
Nginx deve essere riavviato dopo un aggiornamento di PHP?
Nginx di per sé non contiene PHP e non deve riavviarsi per la patch di PHP. Devono riavviarsi tutti i servizi PHP-FPM. Se nel frattempo modifica la configurazione di Nginx, la verifichi con nginx -t prima del ricaricamento.
Perché un pool separato per ogni sito?
Ogni pool gira con un utente proprio e con limiti propri. Se un sito viene compromesso, l'aggressore non arriva senza difficoltà ai file degli altri. Inoltre i valori transitori si possono impostare in modo mirato per ogni pool.
pm.max_requests è una protezione contro PHP2Shell?
No, non sostituisce una patch. Il valore limita quante richieste elabora un worker prima di essere sostituito. Questo può accorciare la durata degli stati nel processo, ma non è un rimedio indicato da php.net.