ARTICOLO

Due falle critiche in PHP: come ho controllato i miei servizi

PHPDockersecurity

Il 21 ottobre il CERT-AGID ha segnalato due vulnerabilità critiche nel core di PHP, CVE-2026-109618 e CVE-2026-109619, con esecuzione di codice da remoto. Lo stesso giorno sono uscite le versioni corrette. Ho controllato i miei servizi: uno era già aggiornato, uno non usa PHP, uno aveva PHP-FPM in un container Docker con l'immagine non ricostruita da settimane. Il terzo è il caso che vale la pena raccontare.

Cosa dicono le fonti ufficiali

Ho preso i dati dal bollettino del CERT-AGID e li ho confrontati con gli advisory del progetto PHP su GitHub (GHSA-w8hw-w9fx-j79w e GHSA-6wmf-4rv5-p3rp), con gli annunci di php.net e con NVD.

Il bollettino del CERT-AGID parla anche di proof of concept in circolazione, "di efficacia non pienamente verificata". Gli advisory ufficiali non dicono che siano pubblici: lo riporto come informazione del CERT-AGID, non l'ho verificata. Per decidere cambia poco: si aggiorna comunque, e subito.

Se vuoi un primo controllo dall'esterno senza aprire il terminale, ho fatto un controllo gratuito della versione PHP: legge quello che il tuo sito dichiara e ti dice se è aggiornato. Non vede tutto, quindi i comandi qui sotto restano il controllo vero.

Prima domanda: che PHP gira davvero

Da terminale:

php -v
php -r 'echo PHP_VERSION, " ", PHP_SAPI, PHP_EOL;'

Attenzione: questo ti dice la versione e il SAPI della riga di comando, cioè cli. Il PHP che serve le pagine può essere un altro binario, con un altro php.ini. Su un server con FPM conviene interrogare direttamente lui:

php-fpm -v
php-fpm -i | grep -i upload_progress

Il nome del binario cambia con la distribuzione (php-fpm8.3 su Debian e Ubuntu, per esempio). In un container Docker: docker compose exec app php-fpm -v, dove app è il nome del tuo servizio.

Dall'esterno, molti server lo dicono da soli:

curl -sI https://tuosito.example | grep -i x-powered-by

Se la risposta contiene qualcosa come X-Powered-By: PHP/8.x.y, il server sta comunicando a chiunque la versione esatta. Il giorno in cui esce un CVE, è il primo filtro che usa chi cerca bersagli. Si spegne con expose_php = Off nel php.ini. Non è una difesa, la versione vulnerabile resta vulnerabile, ma non c'è motivo di regalare l'informazione.

Seconda domanda: mod_php o FPM

Il SAPI cambia il livello di rischio, quindi va verificato sul PHP web, non sulla CLI. Il modo più diretto è un file temporaneo con un nome non indovinabile, da cancellare appena letto:

<?php
echo PHP_VERSION, ' ', PHP_SAPI, ' upload_progress=', ini_get('session.upload_progress.enabled');

apache2handler significa mod_php. fpm-fcgi significa PHP-FPM. Lo stesso dato compare come "Server API" in phpinfo(), che però espone molto di più: se lo usi, toglilo subito.

La mitigazione temporanea, e i suoi limiti

Il bollettino indica session.upload_progress.enabled = 0 come misura temporanea. L'advisory ufficiale è più preciso: vale per CVE-2026-109619, blocca il percorso di attacco dimostrato attraverso la sessione e non gli altri, e richiede il riavvio dei worker perché abbia effetto. Per CVE-2026-109618 l'advisory propone misure diverse, da leggere lì. Tutte le fonti concordano che l'unica correzione completa è aggiornare.

Se hai bisogno di qualche ora prima di aggiornare, spegnila e riavvia FPM o Apache. Se il tuo codice usa la barra di avanzamento degli upload basata sulla sessione smetterà di funzionare: meglio saperlo prima.

Il caso del container: FROM php:8.x-fpm non si aggiorna da solo

Il servizio che aveva il problema era un'applicazione in Docker con un Dockerfile che parte da un tag di ramo, del tipo:

FROM php:8.x-fpm

L'idea diffusa è che un tag "di ramo" come 8.4-fpm o 8.3-fpm ti tenga aggiornato. È vero solo a metà. Sul registry il tag si sposta sull'ultima patch (le immagini ufficiali con le versioni corrette erano disponibili già la sera del 21). Ma sul server Docker usa l'immagine che ha in cache, scaricata il giorno dell'ultima build. Finché non ricostruisci, il container continua a girare con il PHP di settimane prima, qualunque cosa ci sia scritto nel Dockerfile.

E un semplice docker compose build non basta: senza --pull riusa l'immagine base locale. Quello che serve è:

docker compose build --pull
docker compose up -d
docker compose exec app php-fpm -v

L'ultimo comando è la verifica: finché non vedi la versione corretta, non è fatto. Per i servizi che usano un'immagine già pronta, senza build, l'equivalente è docker compose pull seguito da up -d.

Per non dipendere dalla memoria la volta successiva, ci sono due strade che si combinano bene:

Il container ti dà riproducibilità, e la riproducibilità include anche il PHP vecchio, riprodotto fedelmente finché non lo cambi tu. Se usi Laravel in Docker, sullo stesso tema ho scritto di timeout e crash delle code nei container.

Sull'hosting condiviso

Qui il lavoro è del provider, ma il controllo resta tuo. Molti hosting condivisi usano ancora Apache con mod_php, cioè il caso più esposto. Dal pannello di controllo cerca il selettore della versione PHP: verifica che il ramo sia supportato e che la patch indicata sia quella corretta. Se il pannello mostra solo "8.3" senza la patch, il file temporaneo di prima ti dà la versione esatta. Se dopo un giorno o due è ancora vecchia, scrivi al supporto citando i due CVE.

Checklist

Il controllo mi ha preso meno di un'ora. Il servizio vulnerabile non era quello a cui avrei pensato per primo: era quello che funzionava bene da settimane e che nessuno toccava.

← Tutti gli articoli