Il plugin è installato da anni, arriva dalla directory ufficiale, ha migliaia di recensioni positive e si aggiorna da solo. Una notte esce una nuova versione, gli aggiornamenti automatici la installano, e da quel momento il sito ha un amministratore in più che nessuno ha creato. Nessuna vulnerabilità è stata sfruttata, nessuna password del sito è stata rubata: il codice malevolo è entrato dalla porta principale, con la firma dell’autore legittimo.
In un attacco alla filiera il sito non viene violato: viene aggiornato. Per questo le difese pensate contro gli exploit, da sole, non lo vedono arrivare.
È quello che nel mondo del software si chiama attacco alla supply chain, alla catena di fornitura: invece di colpire migliaia di siti uno per uno, l’attaccante compromette il fornitore da cui tutti si riforniscono. In questo articolo vediamo come succede nell’ecosistema WordPress, due casi documentati, come reagisce wordpress.org e, soprattutto, cosa può fare chi gestisce un sito per accorgersene e per ridurre il rischio senza rinunciare agli aggiornamenti.
Tre strade per trasformare un plugin legittimo
Un plugin della directory ufficiale arriva sui siti attraverso un percorso preciso: uno o più sviluppatori con accesso in scrittura (i committer) pubblicano una nuova versione nel repository Subversion di wordpress.org, l’API degli aggiornamenti la annuncia a tutti i siti che hanno il plugin installato, e ogni sito la scarica quando un amministratore preme «Aggiorna» o, sempre più spesso, in automatico. Chi controlla il primo anello controlla tutti gli altri. Le strade per arrivarci sono sostanzialmente tre.
La vendita. Molti plugin gratuiti sono mantenuti da sviluppatori singoli o da piccole aziende che a un certo punto smettono di occuparsene. Un plugin con decine di migliaia di installazioni attive è un asset, e c’è chi lo compra proprio per la base installata: con il repository arriva la possibilità di distribuire codice a tutti quei siti. Il passaggio è legittimo e spesso innocuo, ma chi usa il plugin di solito non ne sa nulla.
L’account dello sviluppatore compromesso. Se la password dell’account di un committer è la stessa usata su un altro servizio violato, l’attaccante può provarla e, se funziona, pubblicare una versione a nome dello sviluppatore. Nessuno ha venduto nulla, ma l’effetto è identico.
La filiera di sviluppo. Tra il computer dello sviluppatore e il repository ci sono spesso altri passaggi: una pipeline di integrazione continua che pubblica in automatico, librerie di terze parti incluse nel pacchetto, un server di aggiornamento proprietario per le versioni premium. Ognuno di questi è un punto in cui il codice può essere alterato prima di arrivare ai siti.
Il caso è diverso da quello dei plugin nulled, dove è l’utente a scaricare un pacchetto da una fonte non ufficiale, ed è diverso anche da una vulnerabilità 0-day, dove il codice è difettoso ma non malevolo. Qui la fonte è quella giusta e il codice fa esattamente ciò che l’autore della versione voleva.
Giugno 2024: cinque plugin e un amministratore in più
Il caso più citato è di fine giugno 2024. Il 24 giugno Wordfence ha pubblicato un’analisi di cinque plugin della directory ufficiale in cui era stato iniettato codice malevolo, partendo da una segnalazione del team di revisione dei plugin su Social Warfare. Le versioni coinvolte, secondo il resoconto ripreso anche da BleepingComputer, erano Social Warfare dalla 4.4.6.4 alla 4.4.7.1, Blaze Widget dalla 2.2.5 alla 2.5.2, Wrapper Link Element dalla 1.0.2 alla 1.0.3, Contact Form 7 Multi-Step Addon dalla 1.0.4 alla 1.0.5 e Simply Show Hooks dalla 1.2.1 alla 1.2.2.
Il codice aggiunto faceva due cose: provava a creare un nuovo account amministratore e ne inviava le credenziali a un server controllato dall’attaccante, e inseriva nel footer del sito un JavaScript che aggiungeva spam SEO. Gli account creati avevano nomi innocui come «Options» e «PluginAuth», facili da scambiare per utenti tecnici di un plugin. È lo stesso schema degli amministratori fantasma che troviamo spesso nelle bonifiche, con la differenza che qui l’accesso iniziale non è servito: l’account lo ha creato il sito stesso eseguendo l’aggiornamento.
La causa è stata chiarita qualche giorno dopo dal team dei plugin di wordpress.org, in un post del 26 giugno 2024: cinque account di wordpress.org erano stati compromessi provando combinazioni di nome utente e password già trapelate in violazioni di altri siti. Nessuna falla nella piattaforma, quindi, ma il più classico riutilizzo delle password, applicato ad account che pubblicano codice su migliaia di siti.
Essential Plugin: una backdoor che ha aspettato sette mesi
Il secondo caso è più recente e mostra l’altra strada, quella della vendita. Secondo l’analisi di Patchstack, il portafoglio di plugin di Essential Plugin, sviluppati dal 2015, è stato venduto nel 2025 attraverso un marketplace di compravendita di siti e progetti. Il primo commit del nuovo proprietario, a settembre 2025, aveva un messaggio rassicurante («Check compatibility with WordPress version 6.8.2») e introduceva in tutti i plugin un endpoint REST accessibile senza autenticazione che scaricava dati da un dominio del venditore e li passava a unserialize().
È un meccanismo di PHP object injection, lo stesso di cui abbiamo parlato a proposito dei payload serializzati: finché il server remoto restituisce dati innocui non succede nulla, e chi legge il codice di corsa vede una funzione di statistiche. La backdoor è rimasta inattiva fino ai primi giorni di aprile 2026; una volta attivata, secondo Patchstack, ha scritto nella radice dei siti un file dal nome simile a quelli del core, wp-comments-posts.php, e ha modificato wp-config.php per iniettare spam.
Il 7 aprile 2026 wordpress.org ha chiuso definitivamente tutti i 31 plugin del portafoglio e ha distribuito un aggiornamento che neutralizzava la backdoor, come ricostruito anche da WordPress.com. Il dettaglio che conta, per chi gestisce siti, è la distanza temporale: sette mesi tra l’introduzione del codice e il suo uso. Un controllo fatto solo «dopo l’aggiornamento, per vedere se il sito funziona» non avrebbe notato nulla.
Come risponde wordpress.org
Quando il team dei plugin viene a sapere che una versione è malevola, gli strumenti a disposizione sono essenzialmente tre. Il primo è la chiusura del plugin nella directory: non si può più scaricare né installare da zero, e la pagina mostra un avviso. La chiusura però non rimuove nulla dai siti che l’hanno già installato, e il plugin chiuso smette semplicemente di ricevere aggiornamenti. Il secondo è l’aggiornamento forzato: in casi gravi wordpress.org può distribuire una versione correttiva anche ai siti che hanno disattivato gli aggiornamenti automatici di quel plugin, com’è successo con Essential Plugin. Il terzo è il ripristino degli account, cioè il blocco degli accessi compromessi e la revisione di chi ha diritto di pubblicare.
Nei mesi successivi all’incidente del 2024, wordpress.org ha anche cambiato le regole per gli autori. Dal 1° ottobre 2024 l’autenticazione a due fattori è obbligatoria per gli autori di plugin e temi, e per il commit sul repository si usa una password SVN dedicata, generata casualmente e revocabile, diversa da quella dell’account. Dopo il caso Essential Plugin sono arrivati altri due cambiamenti: dal 5 giugno 2026 i nuovi rilasci attendono un periodo di raffreddamento prima di essere distribuiti tramite gli aggiornamenti automatici, e da settembre ogni rilascio passa da una revisione di sicurezza automatica che blocca quelli con un punteggio di rischio alto. Nello stesso annuncio il team riporta un caso di fine luglio in cui una versione con una backdoor è stata fermata durante l’attesa e non è mai stata distribuita.
Sono misure utili, ma non coprono tutto: i plugin premium distribuiti dai server dei produttori non passano da wordpress.org, e una backdoor dormiente come quella del 2025 si presenta, al momento del rilascio, come una funzione quasi innocua. Sapere cosa gira sul sito resta compito di chi lo gestisce.
Leggere un aggiornamento prima di installarlo
Il controllo più diretto è anche il più trascurato: confrontare la versione installata con quella nuova. Per i plugin della directory ufficiale ogni versione pubblicata resta scaricabile, quindi il confronto richiede un paio di minuti. Serve soprattutto sapere dove guardare.
# Scaricare la versione installata e quella nuova in una cartella temporanea
cd "$(mktemp -d)"
curl -sSLO https://downloads.wordpress.org/plugin/nome-plugin.1.4.2.zip
curl -sSLO https://downloads.wordpress.org/plugin/nome-plugin.1.4.3.zip
unzip -q -d vecchia nome-plugin.1.4.2.zip
unzip -q -d nuova nome-plugin.1.4.3.zip
# File aggiunti, rimossi o modificati
diff -rq vecchia/nome-plugin nuova/nome-plugin
# Solo le righe aggiunte che toccano utenti, rete, esecuzione e REST
diff -ru vecchia/nome-plugin nuova/nome-plugin \
| grep -nE '^\+.*(wp_insert_user|wp_create_user|set_role|wp_remote_(get|post)|curl_exec|base64_decode|gzinflate|eval\(|unserialize|file_put_contents|register_rest_route|__return_true)'
Le funzioni nell’elenco non sono malevole di per sé, ma un aggiornamento che si presenta come «compatibilità con l’ultima versione di WordPress» e aggiunge una chiamata di rete, un endpoint REST con permission_callback impostato a __return_true o una creazione di utenti merita una lettura attenta. Utile anche confrontare il diff con il changelog: una versione minore che cambia decine di file senza spiegarlo è un segnale da approfondire.
Il repository SVN dice anche chi ha pubblicato. Il registro dei commit riporta il nome utente del committer per ogni revisione, e un nome mai visto prima in un plugin mantenuto da anni dalla stessa persona è esattamente il tipo di cambiamento che la directory non segnala agli utenti. L’API pubblica di wordpress.org restituisce inoltre l’elenco dei contributori dichiarati, da salvare e confrontare nel tempo:
# Ultimi commit: autore, data e file toccati
svn log -l 10 -v https://plugins.svn.wordpress.org/nome-plugin/
# Contributori e data dell'ultimo aggiornamento secondo l'API di wordpress.org
curl -sg 'https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=nome-plugin' \
| jq '{version, last_updated, contributors: (.contributors | keys)}'
Segnali sul sito: checksum, utenti e connessioni in uscita
Se l’aggiornamento è già installato, i controlli si spostano sul sito. Il primo è la verifica dei checksum con WP-CLI, che confronta ogni file dei plugin della directory con l’hash pubblicato da wordpress.org per quella versione. Va letto con attenzione: in un attacco alla filiera la versione malevola è quella ufficiale, quindi i checksum corrispondono. La verifica è utile per un altro motivo: dimostra che i file sul disco sono quelli della versione dichiarata, e restringe l’analisi a una domanda precisa, cioè se quella versione è tra quelle compromesse. Per i plugin premium la verifica non è disponibile e il confronto va fatto con il pacchetto scaricato dall’area clienti del produttore.
# File dei plugin diversi dalla versione ufficiale dichiarata
wp plugin verify-checksums --all
# Versioni installate, da confrontare con gli avvisi di sicurezza
wp plugin list --fields=name,status,version,update_version,auto_update
# Amministratori, dal più recente
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered \
--orderby=registered --order=DESC
Il secondo controllo sono gli utenti: un amministratore registrato nelle ore successive a un aggiornamento, con un nome generico e un indirizzo email estraneo all’organizzazione, va trattato come un indicatore di compromissione finché non si dimostra il contrario. Il terzo sono le connessioni in uscita. Quasi tutte le backdoor distribuite con un aggiornamento devono parlare con qualcuno, per inviare credenziali o ricevere istruzioni. WordPress permette di bloccare le richieste HTTP esterne fatte con le sue API, lasciando passare solo i domini necessari:
// wp-config.php: solo i domini indispensabili possono essere contattati via WP_Http
define( 'WP_HTTP_BLOCK_EXTERNAL', true );
define( 'WP_ACCESSIBLE_HOSTS', 'api.wordpress.org,downloads.wordpress.org,*.stripe.com' );
Il blocco vale solo per le chiamate che passano da wp_remote_get() e simili: un codice che usa direttamente cURL o i socket di PHP lo aggira. Per questo il controllo vero delle connessioni in uscita si fa a livello di sistema, con un firewall in uscita per l’utente del pool PHP-FPM o almeno osservando le connessioni aperte con ss -tnp state established. Il blocco applicativo resta utile come allarme, ma, come spieghiamo parlando dei limiti degli scanner, un controllo che gira dentro WordPress non può vedere tutto.
Aggiornamenti a stadi e versioni bloccate
La reazione istintiva a un attacco di questo tipo è spegnere gli aggiornamenti automatici. È quasi sempre un errore: gli aggiornamenti correggono vulnerabilità che, una volta pubblicate, vengono sfruttate in modo automatico nel giro di poco, e un sito fermo a una versione vulnerabile è un bersaglio facile. La soluzione è governare gli aggiornamenti, non evitarli.
Su un sito importante, lo schema che consigliamo è a stadi. Gli aggiornamenti vengono applicati prima su una copia di staging, che li riceve subito; la produzione li riceve dopo un’attesa di uno o due giorni, durante la quale la copia viene osservata e le versioni nuove vengono confrontate con gli avvisi pubblicati. Gli aggiornamenti di sicurezza urgenti fanno eccezione e si applicano subito, dopo aver letto il diff. I plugin critici per il fatturato, come il checkout di un e-commerce, si gestiscono a mano con la versione bloccata:
# Niente aggiornamento automatico per un plugin specifico
wp plugin auto-updates disable nome-plugin
# Tornare a una versione precisa, già verificata
wp plugin install nome-plugin --version=1.4.2 --force
# Con Composer e WPackagist la versione è fissata nel file di progetto
composer require wpackagist-plugin/nome-plugin:1.4.2
Con Composer, o con un repository Git che contiene l’intera installazione, ogni aggiornamento diventa una modifica esplicita, con un diff da approvare e una storia che dice quando è stata introdotta ogni versione. È il modello che usiamo quando gestiamo i siti dopo una bonifica, insieme alle altre misure della checklist di hardening.
Tenere d’occhio i cambi di proprietà
L’ultimo tassello è il più semplice da automatizzare e quello che quasi nessuno fa. Per ogni plugin installato si salva periodicamente l’elenco dei contributori restituito dall’API di wordpress.org e il nome del committer delle ultime revisioni, e si genera un avviso quando cambiano. Un plugin che cambia autore non è per forza compromesso, ma è il momento giusto per leggere con più attenzione i rilasci successivi, e magari per spostarlo tra quelli con la versione bloccata.
Vale anche la regola inversa: meno plugin, meno fornitori da sorvegliare. Un plugin marginale, fermo da due anni, è il candidato ideale per un acquisto a basso prezzo; rimuoverlo riduce la superficie più di qualunque controllo.
Se un plugin che usi compare in un avviso di questo tipo, l’aggiornamento alla versione corretta toglie il codice malevolo ma non ciò che quel codice ha già fatto: gli amministratori creati, i file scritti nella radice, le modifiche a wp-config.php e al database restano al loro posto. È il lavoro che facciamo nelle bonifiche, a livello applicativo e di sistema, con un’analisi dei log assistita da modelli di intelligenza artificiale eseguiti in locale per ricostruire cosa è stato fatto e quando. Se hai un dubbio su un aggiornamento recente, scrivici: la pulizia è garantita in 6, 12 o 24 ore per ogni installazione WordPress.