Tutorial php2shell aggiornare php terminale

Il 21 ottobre 2026 il progetto PHP ha corretto due falle critiche, CVE-2026-109618 e CVE-2026-109619 («php2shell»): permettono a chiunque, senza login, di eseguire comandi sul server attraverso PHP-FPM, mod_php o CGI. Sono già sfruttate. Ecco come verificare se si è esposti e come aggiornare senza fermare i siti, con i passaggi che abbiamo seguito sui nostri server.

Cosa sappiamo

Ramo di PHPVersione corretta
8.58.5.12
8.48.4.27
8.38.3.36
8.28.2.35
7.4 – 8.1nessuna versione ufficiale: solo correzioni non ufficiali di distribuzioni e fornitori

Il team di sicurezza di PHP assegna a entrambe un punteggio CVSS 4.0 di 9,2. I primi tentativi di attacco sono stati osservati il 17 ottobre, quattro giorni prima della correzione; dal 21 ottobre gli attacchi sono diffusi. L’unica soluzione completa è aggiornare.

Primo passo: quale PHP serve davvero i siti

È l’errore più comune: si scrive php -v, si legge una versione corretta e si chiude lì. Ma php -v mostra il PHP da terminale, che spesso non è quello che serve le pagine. Su un server con più versioni (per esempio con ISPConfig o Plesk) ogni sito ha il suo pool PHP-FPM, e la versione del pool è quella che conta per questa falla.

ls /etc/php/
for v in /etc/php/*/; do echo "== $v"; ls "$v"fpm/pool.d/ 2>/dev/null; done
systemctl list-units 'php*-fpm*' --state=running

Ogni versione con un pool attivo va aggiornata. Quelle installate solo da terminale vanno aggiornate lo stesso, ma non espongono i siti.

Secondo passo: da dove arriva PHP

Molte guide dicono che su Debian e Ubuntu gli aggiornamenti di sicurezza si installano da soli. Vale solo se:

  • gli aggiornamenti automatici (unattended-upgrades) sono davvero attivi;
  • PHP viene dal repository ufficiale della distribuzione.

Chi ha più versioni di PHP le installa quasi sempre da un repository esterno, come quello di Ondřej Surý (packages.sury.org). Quel repository riceve le correzioni presto, ma apt non le considera aggiornamenti di sicurezza: gli aggiornamenti automatici configurati «solo sicurezza» le saltano.

apt-cache policy php8.3-fpm | head -6

La riga con l’indirizzo dice da dove arriva il pacchetto.

Terzo passo: aggiornare solo PHP, prima con la simulazione

Un apt upgrade generico aggiorna tutto il sistema insieme: se qualcosa si rompe, non si sa cosa. Meglio aggiornare solo i pacchetti PHP, e prima fare una simulazione (dry run): apt-get -s mostra cosa cambierebbe senza toccare niente.

sudo apt-get update
P=$(apt list --upgradable 2>/dev/null \
  | grep -o '^php[^/]*' | tr '\n' ' ')
echo "$P"
sudo apt-get -s install --only-upgrade $P \
  | grep -E '^(Inst|Remv)' | grep -v '^Inst php'

L’ultimo comando deve restare vuoto: nessun pacchetto che non sia PHP, nessuna rimozione. Se compare qualcosa, ci si ferma e si capisce perché.

Poi l’aggiornamento vero e il riavvio dei pool, che interrompe i siti per pochi secondi:

sudo apt-get install -y --only-upgrade $P
# una riga per ogni versione con pool attivi
sudo systemctl restart php8.3-fpm
systemctl is-active php8.3-fpm
php8.3 -v | head -1

Il riavvio è indispensabile: finché i processi PHP-FPM restano quelli di prima, il codice vulnerabile resta in memoria.

Quarto passo: verificare i siti, a mano

for u in sito1.example sito2.example; do
  echo "$u $(curl -s -o /dev/null -w '%{http_code}' https://$u/)"
done

Un 200 dice che il sito risponde, non che funziona. Poi si entra nel pannello di ogni applicazione e si prova un’operazione vera: login, salvataggio, apertura di una pagina dinamica.

Se non si può aggiornare subito

Il progetto PHP indica alcune impostazioni che riducono il rischio senza eliminarlo, da usare solo come ponte: session.upload_progress.enabled=0 per CVE-2026-109619 e un memory_limit che le applicazioni non possono cambiare per CVE-2026-109618, con il riavvio di tutti i processi PHP. Un firewall applicativo (per esempio le regole già pubblicate da Cloudflare) aiuta a guadagnare tempo. Non sostituiscono l’aggiornamento.

Dopo: cercare tracce

Se una versione vulnerabile è rimasta esposta dopo il 17 ottobre, aggiornare non basta: bisogna controllare che nessuno sia entrato nel frattempo.

sudo find /percorso/dei/siti -name '*.php' -newermt '2026-10-17' \
  -not -path '*/vendor/*' -not -path '*/cache/*' | head -50
sudo crontab -l -u <utente-del-sito>
ps -eo user,pid,etime,cmd | grep '<utente-del-sito>' | grep -v php-fpm
sudo journalctl -u 'php*-fpm' --since '2026-10-17' \
  | grep -i -E 'segfault|signal|out of memory'

Un file PHP nuovo in una cartella di immagini, un cron che nessuno ha creato, un processo sh o curl dell’utente del sito: sono gli indizi da cercare. Un file sospetto si sposta in quarantena, non si cancella, così resta la prova.

Se l’applicazione vive in un repository git

Capita spesso che lo stesso giorno arrivi anche un avviso su una libreria dell’applicazione. Sul server la correzione si fa subito, ma il repository deve restare la fonte di verità, altrimenti al prossimo git pull la correzione sparisce o va in conflitto.

Sul server, con l’utente del sito (mai root), si aggiorna solo il pacchetto interessato:

cd /percorso/del/progetto
git status --short       # vuoto prima di iniziare
composer update vendor/pacchetto --with-dependencies --no-dev
composer show vendor/pacchetto | grep versions

Si prova l’applicazione, poi si salva la differenza in un file: git diff > /tmp/correzione.patch. Sul computer di sviluppo la patch diventa un branch e una pull request, con i test. Dopo il merge, sul server: git checkout -- . && git pull. Il clone torna identico al repository, con la correzione dentro.

Un’insidia che ci ha fatto perdere qualche minuto: Composer usa il PHP da terminale, che può non avere le estensioni del PHP del sito (curl, intl, zip) e si rifiuta di procedere. La soluzione pulita è lanciare Composer con la stessa versione del sito (php8.3 $(command -v composer) …). In emergenza si può usare --ignore-platform-req=ext-*, che ignora le estensioni mancanti ma continua a controllare la versione di PHP. Mai --ignore-platform-reqs senza specificare cosa: ignora anche la versione.

Non farlo una volta sola

Questa volta l’avviso è arrivato via email. La volta successiva potrebbe non arrivare. Due controlli automatici, ogni notte, bastano a non dipendere dalla fortuna:

  • pacchetti PHP in attesa di aggiornamento, contati a parte, perché il repository esterno non li segnala come sicurezza;
  • composer audit sul progetto, che confronta le librerie installate con gli avvisi pubblicati.

Se uno dei due trova qualcosa, deve arrivare un avviso a una persona: un ticket, una email, una notifica. Un controllo che nessuno legge non è un controllo.

La procedura completa che usiamo, con i comandi di verifica, il rollback e i due controlli automatici pronti da installare, la mandiamo su richiesta. Se invece vi serve una mano a verificare un server, scriveteci.

La procedura è quella che usiamo sui nostri server, scritta come script bash da leggere riga per riga prima di usarli. Contiene la simulazione, che elenca cosa cambierebbe e si ferma se c’è altro oltre a PHP; l’aggiornamento dei soli pacchetti PHP, con le versioni precedenti annotate per il rollback; la verifica dei siti dopo il riavvio, un dominio alla volta; i due controlli notturni, pacchetti in attesa e composer audit, che avvisano solo quando qualcosa cambia; i parametri in un unico file, per usarla su un server diverso dal nostro. La condividiamo con chi gestisce server: raccontateci che sistema usate e ve la mandiamo noi.

Il metodo in cinque regole

  1. Si aggiorna solo PHP, non tutto il sistema: meno cose cambiano, meno cose si rompono.
  2. Prima la simulazione, poi il comando vero: si legge cosa cambierebbe, e solo dopo si decide.
  3. Una versione per sito, la stessa in tutto il sito: FPM, cron, scheduler e Composer sullo stesso PHP.
  4. Si verifica sito per sito, dopo il riavvio: una pagina che risponde non è ancora una pagina giusta.
  5. Il controllo è automatico, l’avviso arriva a una persona: ogni notte, pacchetti in attesa e librerie.

Aggiornare non è un’emergenza da gestire quando arriva l’email: è una routine che, quando l’emergenza arriva, è già stata fatta.

Fonti