Situazione attuale: PHP2Shell. Due vulnerabilità critiche di PHP (CVSS 9,2) consentono codice dannoso senza autenticazione. Patch dal 21/10/2026.Che cosa fare ora →

Il ciclo di vita della richiesta: startup, esecuzione, shutdown

Come PHP avvia i processi, elabora le richieste e ripulisce nel frattempo. Con le fasi da MINIT a MSHUTDOWN e il legame con CVE-2026-109619.

Stato 6 min di letturaConoscere PHP

In breve: il PHP Request Lifecycle ha due livelli. Una volta per processo PHP carica le proprie estensioni (fase MINIT) e alla fine le smonta di nuovo (MSHUTDOWN). Per ogni singola richiesta esiste una propria fase di avvio e di pulizia (RINIT e RSHUTDOWN). Nell'esercizio web un worker elabora molte richieste una dopo l'altra, perciò tra due richieste PHP deve reimpostare in modo affidabile tutto lo stato della richiesta. Proprio su questo confine si trovava CVE-2026-109619, la seconda vulnerabilità PHP2Shell.

Due livelli: il processo e la richiesta

Per i non tecnici aiuta un'immagine: un worker PHP è come uno sportello di un ufficio pubblico. Lo sportello viene allestito una volta al mattino, con moduli, timbri e computer. Poi arrivano molti visitatori, uno dopo l'altro. Dopo ogni visitatore il tavolo deve essere vuoto, perché il successivo non veda documenti altrui.

PHP funziona allo stesso modo. All'avvio di un processo carica estensioni e impostazioni uguali per tutte le richieste. Ogni richiesta riceve poi la propria memoria, le proprie variabili e i propri dati della richiesta, come $_GET, $_POST o $_FILES. Al termine della richiesta PHP libera questa memoria. Il PHP Internals Book lo riassume in breve: PHP dimentica la memoria della richiesta al termine di ogni richiesta.

Le fasi del PHP Request Lifecycle in sintesi

I nomi provengono dall'interno di PHP; ogni estensione può agganciarsi a queste fasi.

Fase Quando viene eseguita Quanto spesso Compito tipico
MINIT (Module Init) all'avvio del processo una volta per processo registrare le estensioni, creare le voci ini, dati permanenti
GINIT alla creazione delle globali per processo, con i thread per thread preparare le variabili globali di un'estensione
RINIT (Request Init) all'inizio di ogni richiesta per richiesta preparare la memoria e lo stato della richiesta
Esecuzione nel frattempo per richiesta gira il Suo codice PHP
RSHUTDOWN al termine di ogni richiesta per richiesta liberare le risorse della richiesta
PRSHUTDOWN dopo RSHUTDOWN per richiesta, usata raramente ultime operazioni di pulizia
MSHUTDOWN alla chiusura del processo una volta per processo liberare i dati permanenti

Una frase importante del PHP Internals Book per gli sviluppatori di estensioni: le globali non vengono svuotate automaticamente dopo ogni richiesta. Ciò che deve essere fresco a ogni richiesta va reimpostato in RINIT. Questa regola mostra perché il confine tra due richieste sia un punto critico per la sicurezza.

Anche al termine di una richiesta l'ordine è stabilito: prima vengono eseguite le funzioni di shutdown che Lei ha registrato con register_shutdown_function(), poi i distruttori, poi viene svuotato il buffer di output. Solo dopo segue RSHUTDOWN.

Perché i worker vengono riutilizzati

Dalla riga di comando un processo elabora esattamente una richiesta e termina. Nell'esercizio web sarebbe troppo lento. PHP-FPM, Apache con mod_php, LiteSpeed e FrankenPHP tengono quindi pronti dei worker che servono molte richieste di seguito. MINIT viene allora eseguita una volta, RINIT e RSHUTDOWN migliaia di volte. Come FPM governa i propri worker lo spiega PHP-FPM spiegato.

In PHP-FPM pm.max_requests stabilisce dopo quante richieste un worker si riavvia. Il valore predefinito è 0, cioè illimitato. Il manuale indica l'impostazione come rimedio contro le perdite di memoria nelle librerie di terze parti:

; Pool FPM: riavviare i worker dopo 500 richieste (contro le perdite di memoria)
pm = dynamic
pm.max_children = 20
pm.max_requests = 500

La modalità worker di FrankenPHP va ancora oltre: lì l'intera applicazione si avvia una volta e resta in memoria. Lo stato dell'applicazione sopravvive allora anche a livello PHP. L'esempio mostra la differenza:

<?php
function anfrageZaehler(): int
{
    static $zaehler = 0;   // nell'esercizio FPM classico di nuovo 0 a ogni richiesta
    return ++$zaehler;     // in modalità worker il valore continua a contare tra le richieste
}
echo anfrageZaehler();

Nell'esercizio classico la chiamata restituisce sempre 1, perché PHP scarta la memoria della richiesta. In modalità worker il valore cresce finché il worker è vivo. La documentazione di FrankenPHP consiglia perciò di reimpostare in modo consapevole lo stato legato alla richiesta tra una richiesta e l'altra.

Dove agisce CVE-2026-109619

CVE-2026-109619 non riguarda il codice della Sua applicazione, ma PHP stesso. Secondo php.net, uno stato interno appartenente alla richiesta poteva sopravvivere alla liberazione della sua memoria. Una richiesta successiva nello stesso worker poteva usarlo prima che l'inizializzazione lo reimpostasse. Nell'immagine: sul tavolo dello sportello era rimasto un foglio che avrebbe dovuto essere già stato tolto. Non pubblichiamo volutamente dettagli sullo sfruttamento; l'inquadramento si trova in CVE-2026-109619.

Le misure transitorie di php.net per questa vulnerabilità agiscono su vie confermate, non sono una protezione completa:

  1. session.upload_progress.enabled=0 in php.ini, nel pool FPM o nella configurazione del server, poi riavviare tutti i worker; non tramite ini_set() o .user.ini.
  2. Su PHP 8.5 inoltre fatal_error_backtraces=0 e riavvio.
  3. Rimuovere le impostazioni non valide o obsolete per richiesta che generano messaggi prima dell'inizializzazione. Nascondere soltanto i messaggi non aiuta, secondo php.net.
  4. Correggere nell'applicazione gli errori fatali e i timeout attivabili dall'esterno, anche nei callback e nell'elaborazione di upload e importazioni.

Il vero rimedio è l'aggiornamento a PHP 8.5.12, 8.4.27, 8.3.36 o 8.2.35. Poiché l'errore è nel worker in esecuzione, l'effetto si ha solo dopo un riavvio di tutti i processi; i passaggi sono in Riavviare PHP-FPM e tutti i worker.

Consiglio

Dopo il riavvio verifichi con ps -eo pid,lstart,cmd | grep php che tutti i processi PHP siano stati avviati dopo il momento dell'aggiornamento.

Che cosa significa il ciclo di vita per l'esercizio e lo sviluppo

Dal modello si ricavano regole chiare. Per gli amministratori: un aggiornamento è efficace solo quando non gira più alcun processo con il vecchio codice. Ogni modalità di esecuzione con worker di lunga durata richiede un riavvio documentato. Per gli sviluppatori: il codice che gira nelle modalità worker non deve lasciare stato legato alla richiesta in variabili statiche o singleton, come dati utente o autorizzazioni. Per entrambi vale: conservare i log dal 20/10/2026, per poter valutare in seguito se prima della patch sia successo qualcosa.

Come aiuta concretamente PHP SOS?

Con il servizio di patch PHP mettiamo in pratica il modello:

  • Determinare la modalità di esecuzione: accertiamo se girano PHP-FPM, mod_php, LiteSpeed, FrankenPHP o php-cgi e come vengono gestiti i worker.
  • Aggiornamento e riavvio: installiamo la versione corretta e riavviamo ogni worker in modo controllato.
  • Verifica dei processi: dimostriamo, in base agli orari di avvio e alle versioni, che non resta alcun vecchio processo.
  • Verificare le modalità worker: nelle applicazioni di lunga durata controlliamo se lo stato legato alla richiesta viene reimpostato correttamente.

Domande frequenti

PHP avvia un nuovo processo per ogni richiesta?

Dalla riga di comando sì, nell'esercizio web di solito no. PHP-FPM, Apache con mod_php, LiteSpeed o FrankenPHP lasciano che un processo elabori molte richieste una dopo l'altra. Tra una richiesta e l'altra PHP ripulisce lo stato della richiesta.

Qual è la differenza tra MINIT e RINIT?

MINIT viene eseguita una volta, quando il processo PHP si avvia, e crea strutture permanenti. RINIT viene eseguita all'inizio di ogni singola richiesta e prepara ciò che serve solo a quella richiesta.

pm.max_requests aiuta contro CVE-2026-109619?

php.net non indica riavvii più frequenti dei worker come misura transitoria. L'impostazione limita soltanto quante richieste un worker elabora prima di riavviarsi. Contro la vulnerabilità aiuta l'aggiornamento, seguito dal riavvio di tutti i worker.

Che cos'è la modalità worker di FrankenPHP?

Nella modalità worker l'applicazione si avvia una sola volta e rimane in memoria. Ciò fa risparmiare tempo, ma variabili statiche, globali e cache restano tra una richiesta e l'altra. Secondo la documentazione di FrankenPHP, lo stato legato alla richiesta va quindi reimpostato in modo consapevole.

Fonti

Contatto immediato

Ogni ora conta. Ci scriva adesso.

Descriva brevemente sistema, versione di PHP e quanto osservato. Ci faremo sentire, chiariremo la situazione e lavoreremo da remoto fino alla chiusura e alla documentazione dell'incidente.

Aiuto immediato