Come ci siamo arrivati
- Agosto 2026 — L’incidente
- Un malware entra da un WordPress del vecchio server e si allarga a tutti i siti: file riscritti, mu-plugin nascosti, operazioni pianificate in wp-cron, amministratori fantasma. Pulire file per file non dà garanzie: si decide di ricostruire.
- La bonifica
- Sul vecchio server ogni sito diventa un backup bonificato: archivi cifrati, checksum, una scansione e un esito (OK, VERIFICARE, BLOCCATO, ERRORE), tutto sulla Storage Box.
- web1
- Un server nuovo, preparato da un solo script: SSH solo a chiave, firewall, un utente Linux per sito, codice in sola lettura, backup notturno, controlli ogni notte. Al posto di Plesk, pochi comandi scritti per questo server.
- I ripristini
- I siti tornano uno alla volta: WordPress e plugin gratuiti riscaricati da wordpress.org, dal backup solo contenuti, upload e temi, poi controlli e blocco.
- 23 settembre — La dashboard
- Undici requisiti (REQ-001…011): un agente sul server che conosce solo un elenco chiuso di operazioni, un pannello senza rete raggiungibile solo dal tunnel SSH, e sopra siti, backup, ripristino, accessi, registro. Tutti collaudati su web1.
- 27 settembre — La sicurezza, fasi 1 e 2
- Impronta del codice, controlli del database, visore, vulnerabilità da Wordfence (40.756 voci il primo giorno), regole di nginx e fail2ban, pagina Sicurezza, controllo ogni ora. Il primo controllo completo: 54.591 file in 38 secondi.
- 28 settembre — Chiudere le porte
- Una domanda: «il sito è bloccato, ma dall’amministrazione posso ancora cambiare tutto?». Sì, perché il database resta scrivibile. Nascono l’amministrazione chiusa, aperta a tempo, e il controllo degli script nascosti nelle pagine di Elementor. Nello stesso giorno: aggiornare i plugin uno per uno e riconoscere quelli a pagamento.
- 29 settembre — wordpress.org nella dashboard
- Cercare e installare plugin e temi come da wp-admin, ma con una regola in più: se l’ultima versione ha vulnerabilità senza correzione, non si installa. Poi Attiva per i temi, e Disattiva, Attiva ed Elimina per plugin e temi: l’eliminazione disinstalla il plugin, ma prima fa una copia del sito e i file non si cancellano. E questo sito: il primo tema installato e attivato con i comandi nuovi è quello che state guardando.
- 29 settembre — Dai controlli ai rimedi
- Trovare un file infetto non basta: bisogna toglierlo senza perdere nulla. Nascono la quarantena, da cui ogni file si può rimettere, le reinstallazioni da wordpress.org con i checksum, il ripristino di un file da una copia, la ricostruzione di tutto il codice con il sito in funzione e il cambio dei segreti, che chiude anche le sessioni aperte. Collaudati su un sito di prova infettato apposta.
- 29 settembre — Anche il server sotto controllo
- Dopo i siti, il server: utenti, chiavi SSH, permessi di sudo, porte, servizi, cron, file SUID e i file di ogni pacchetto confrontati con un riferimento approvato, ogni ora quello che costa poco e ogni notte il resto. Per provare un programma alterato senza toccare quelli veri, il collaudo ha costruito un pacchetto finto e l’ha alterato apposta.
- 30 settembre — La storia del codice
- L’impronta dice se il codice è quello approvato, ma non ricorda com’era prima: su un sito, le modifiche al tema fatte il 21/09 non si potevano più vedere. Ora ogni approvazione è una versione git, con chi e perché; la storia di ogni sito parte dalle copie dei backup ancora presenti. Il codice di un sito sta in poche decine di MB compressi, anche con 25.000 file.
- 30 settembre — Tornare indietro
- Con la storia, la dashboard impara a tornare indietro: le versioni si sfogliano per componente, con le righe cambiate di ogni file, e un file, una cartella o tutto il codice tornano com’erano in una versione, con quello di adesso in quarantena. Il collaudo ha riportato un sito di prova a una versione precedente in 11 secondi, con i checksum di WordPress verificati.
- 30 settembre — Quanto pesa un sito
- La dashboard sapeva se un sito è sano, non quanto occupa. Ora ogni scheda ha lo spazio diviso per parte, upload, plugin, tema attivo, database, misurato ogni notte e tenuto giorno per giorno: la base per i grafici che verranno. Misurare un sito da 30.000 file richiede meno di mezzo secondo, e il totale coincide con quello di
du. - 30 settembre — Una versione di PHP per sito
- Finora tutti i siti usavano la stessa versione di PHP, quella di Debian. Ora sul server possono stare più versioni, solo quelle che ricevono ancora correzioni, e ogni sito sceglie la sua dalla scheda. Il cambio dura meno di un secondo; se con la versione nuova il sito dà errore, la piattaforma rimette quella di prima e lo dice. Nel collaudo un sito di prova è andato da 8.4 a 8.3 e ritorno in 1,6 secondi.
- 30 settembre — I grafici
- Le pagine dicevano com’è un sito adesso; ora dicono anche come sta cambiando: spazio, tentativi di attacco, tempo di risposta, disco, backup, indirizzi bloccati e allarmi, giorno per giorno. Ogni grafico nasce dalla tabella dei suoi dati, che resta sotto: i numeri non possono divergere, e senza JavaScript resta la tabella. Un giorno senza misura è una fascia grigia, mai uno zero. Il collaudo ha contato a mano, sui log, i tentativi di 28 giorni di sito: tutti uguali.
- 1 ottobre — L’amministratore con riserva
- Un terzo ruolo tra amministratore e lettore: fa solo le operazioni delle categorie che gli assegna un amministratore, e ognuna arriva subito come notifica. Il pannello non ha rete, quindi la manda l’agente sul server; il permesso si controlla in un punto solo, prima di chiamarlo.
- 1 ottobre — phpMyAdmin dentro la dashboard
- Il database di ogni sito si apre dalla scheda, senza password: un utente MariaDB che vale un’ora e un solo database, e un collegamento che vale una volta. phpMyAdmin non ha porte: passa dal pannello, su un indirizzo suo (pma.localhost) perché le due sessioni restino separate. Al primo collaudo il cookie della sessione partiva «secure», come vuole l’irrobustimento di PHP per i siti in HTTPS, e sul tunnel in HTTP non tornava indietro. Poi, nel browser vero, era «SameSite=Strict»: arrivando dalla dashboard, che per il browser è un altro sito, il cookie si perdeva nel primo passaggio. Ora è Lax; i collaudi con curl non se ne accorgono, per questo phpMyAdmin si prova anche con un browser.
- 2 ottobre — Verso la produzione
- Per ogni sito una relazione da girare a chi prepara il nuovo hosting: ambiente, wp-config senza segreti, servizi di terzi, plugin e cose da fare, in PDF scritto da web1 senza librerie o in Markdown. I problemi del sito restano nei controlli interni della pagina, mai nei file. Con lei le chiavi e salt nuove. La messa online di un sito su web1 è durata poche ore: web1 non ospita siti in produzione, e lì ogni sito resta con la password di anteprima.
- Due regole per tutta la dashboard: ogni attesa si vede (scheletro di caricamento per ciò che arriva dopo, bottoni in attesa anche quando portano a un’altra pagina) e le tabelle chiave/valore hanno sempre il filetto dopo la prima colonna.
- 2 ottobre — Spegnere un sito
- Un sito si spegne in due passi: disattivato resta intero ma fermo (web, PHP, SFTP e database), eliminato sparisce dal server lasciando una copia e un archivio. Dalla copia, in Backup, il sito si ricrea.
- 3 ottobre — La produzione, sul serio
- web1 resta il server di sviluppo; i siti finiti andranno su server dei clienti che la dashboard crea, mette in sicurezza e sorveglia. Prima del codice, il piano: dodici requisiti e una ricerca sui fornitori. La ricerca ha trovato subito un rischio vero già presente: i PHP dei siti potevano interrogare il servizio dei metadati del cloud. Chiuso lo stesso giorno.
- 5 ottobre — La cassaforte
- Le credenziali dei fornitori aprono server veri: stanno in una cassaforte cifrata con
age, che si apre con una chiave personale tenuta nel password manager, per poco tempo e solo se la notifica arriva. Le chiavi nascono sul PC di chi le usa, mai sul server; nessuna pagina, comando o registro mostra un valore. La prima versione mostrava l’impronta della cassaforte accorciata, con i puntini in mezzo: un’impronta serve a un confronto, e accorciata non si confronta. Ora è intera, come quelle di SSH. - Lo stesso giorno la cassaforte si è completata: una prova delle notifiche entro 30 giorni per aprirla, il deposito da custodire fuori dal server con la prova di recupero, il cambio d’identità, e il passaggio all’esercizio. Da lì sudo e gruppi del server restano quelli approvati, o la cassaforte non si apre. Una sorpresa: anche root risultava «con sudo senza password», per una regola del fornitore cloud. Per root non vuol dire nulla, e ora non conta più.
- 5 ottobre — Il primo collegamento
- Un server creato a mano si aggiunge con una configurazione d’avvio che contiene una chiave host provvisoria: al primo collegamento la dashboard riconosce il server giusto, poi la sostituisce con quella definitiva. Ogni cambio degli accessi torna indietro da solo dopo 5 minuti, se la dashboard non si ricollega a confermarlo. Prima di un server vero, il collegamento è stato provato contro un server simulato e con un sshd di prova. Una lezione: la cassaforte toglie l’a capo finale dai valori incollati, e per una chiave SSH conta.
- 5 ottobre — Strumenti firmati
- I server di produzione ricevono gli script dal server di sviluppo, ma li installano solo se il loro elenco, con l’impronta di ogni file, è firmato con la chiave di rilascio dell’amministratore, che non sta su nessun server. Chi riuscisse a cambiare uno script sul server di sviluppo non lo porterebbe in produzione. Poi la messa in sicurezza: profilo di produzione, password di sudo consegnata una volta, porte provate da fuori una alla volta. Una svista trovata prima del primo server vero: il ritorno automatico di 5 minuti partiva prima del profilo, che su un server nuovo dura di più, e avrebbe rimesso gli accessi di prima a metà lavoro. Ora dura quanto il profilo e riparte da 5 minuti alla fine.
- 6 ottobre — Un sito da un altro hosting
- Per lavorare su un sito che è in produzione altrove bastano i suoi backup: archivio e dump si caricano a pezzi, con la ripresa se la connessione cade, e l’analisi li apre senza eseguire nulla. Il sito nasce solo dopo aver letto la relazione: cosa arriva, cosa resta fuori e quali servizi resteranno attivi anche in sviluppo, perché la copia è fedele. Il collaudo ha importato questa guida da uno zip e da un rar. Ha trovato le traduzioni di WordPress scambiate per codice offuscato (una riga lunghissima è normale in quei file), il sito in inglese perché la lingua va cercata dove la cerca WordPress, e un mu-plugin del vecchio hosting che non si poteva rimettere dalla quarantena perché la sua cartella era sparita con lui.
- 7 ottobre — L’esportazione
- Un sito finito si porta su un altro hosting con due file: lo zip del sito e il database, con il dominio di produzione al posto di quello di sviluppo. Il collaudo su un sito vero ha trovato due cose. wp-cli non cambia l’indirizzo quando sta nelle chiavi di un array serializzato: ora un controllo finale lo cerca riga per riga e lo segnala. E nei database di due siti c’erano ancora le opzioni con cui il malware di agosto si ricostruiva: i suoi file non c’erano più, ma il suo diario sì, scritto sul vecchio server prima dello spostamento. Adesso il controllo del database le segnala e ripristini, importazioni ed esportazioni le tolgono. Nel pomeriggio, su un sito più grande, l’esportazione si è fermata sul database: la versione nuova di MariaDB scrive un record per riga, e il filtro pensato per le definizioni delle tabelle girava senza fine su un record lungo. Ora i record passano intatti e il lavoro dice a che punto è.
- 8 ottobre — La password che non arrivava
- «Su alcuni browser l’anteprima va in loop.» I log hanno raccontato una storia diversa da quella che ci si aspettava: nessuna password sbagliata, nessun blocco di fail2ban; nei casi in loop il browser la password non la mandava proprio. Browser dentro le app, antivirus e proxy che tolgono l’intestazione, o semplicemente un browser che dimentica. Al posto della finestra del browser ora c’è una pagina d’accesso con un cookie firmato: stesse credenziali, funziona ovunque, e se il browser non tiene il cookie lo dice invece di girare in tondo. Con lei gli indirizzi fidati, l’apertura a tutti per qualche ora e l’editor di wp-admin che segue il blocco del codice. Il collaudo ha ritrovato una vecchia conoscenza: la regola di fail2ban che non vedeva nulla perché cercava la data, già tolta da fail2ban stesso.
- 9 ottobre — Un tunnel che porta in un posto solo
- Un collaboratore con il Mac deve usare la dashboard. Prima servivano un utente sul server e qualche comando da amministratore; ora la chiave si incolla nella pagina Utenti, e la pagina prepara le righe da mandargli. Il tunnel arriva alla dashboard e basta: niente terminale, niente file, niente altri servizi del server.
- 12 ottobre — Il primo server vero
- Il primo server di produzione creato su Hetzner si è collegato e poi ha chiuso fuori tutti. Il server non c’entrava. La cartella delle chiavi nasceva con permessi che SSH, quando le legge con l’identità dell’utente, non può attraversare; e il ritorno automatico, scattato come doveva, rimetteva le chiavi di prima intestate a root, illeggibili per gli utenti. Le prove partivano da una cartella già pronta e non lo vedevano: ora partono da quella sbagliata e controllano il proprietario dei file. Per non perdersi tra i passi, la Produzione ha una scheda Guida. In serata la pubblicazione: invece di un trasporto nuovo, l’esportazione già collaudata per gli altri hosting, con il dominio del cliente al posto di quello di sviluppo; sul server il sito nasce dietro l’anteprima e si apre da solo quando il DNS punta lì. Un sito rimasto a metà si toglie da sé: sul server di produzione non c’è la copia che l’eliminazione normale richiede.
Cose imparate lungo la strada
La porta chiusa che chiudeva anche la dashboard
Per il tunnel dei collaboratori sembrava naturale vietare l’inoltro verso le porte del server e permettere solo quello verso la dashboard. Il server SSH però tiene le due cose in un unico elenco: vietando le porte, la dashboard non passava più. La regola giusta nomina l’unica destinazione permessa; il collaudo prova che il resto, porte e altri servizi, resta chiuso. Una regola di sicurezza si prova anche dal lato di ciò che deve funzionare.
Il RAR a metà
Un RAR diviso in più parti, caricato con la sola prima, si apriva senza errori: il programma estraeva quello che c’era e si fermava. L’analisi diceva «nell’archivio non c’è WordPress», vero ma fuorviante. Ora l’intestazione del RAR si legge prima: diviso in parti o cifrato, la risposta dice cosa rifare. Un messaggio d’errore giusto vale quanto il controllo che lo produce.
Il grafico che mentiva senza volerlo
La prima versione dello spazio nel tempo impilava il database sopra i file: la linea più alta era il totale, ma la legenda la chiamava «Database», e chi la guardava leggeva un database di quasi due gigabyte. Ora sono due linee, ciascuna con il suo valore scritto in fondo. Un grafico si guarda come lo guarderebbe chi non l’ha fatto.
La prova che non falliva
Per collaudare il ritorno automatico serviva un sito che con PHP 8.3 si rompesse: un file che usa una funzione nata con PHP 8.4. Ma con la 8.3 il sito funzionava lo stesso, perché WordPress quella funzione la fornisce da sé alle versioni più vecchie. La prova vera è arrivata con una sintassi che la 8.3 non sa leggere. Un collaudo che passa al primo colpo va guardato due volte.
Il pacchetto che non voleva vicini
La prima installazione di una seconda versione di PHP si è fermata subito: il pacchetto comune di Debian dichiara di non poter convivere con nessun’altra versione. La soluzione è prendere quel solo pacchetto dalla stessa sorgente delle altre versioni, lasciando a Debian la versione predefinita. I siti non se ne sono accorti: il servizio non si è nemmeno riavviato.
Gli upload dentro una cartella
Preparando il collaudo delle versioni è saltato fuori un caso: ripristinare da una versione tutta la cartella wp-content avrebbe messo in quarantena anche gli upload, che nelle versioni non ci sono, lasciando il sito senza immagini. Corretto prima di arrivare su un sito vero: le cartelle scrivibili dentro il percorso restano dove sono, e il collaudo lo verifica.
Una porta che cambia a ogni avvio
Il primo giro del controllo del server ha trovato containerd, il motore dei container, in ascolto su una porta locale scelta a caso a ogni avvio. Per non dare un falso allarme a ogni riavvio, le porte alte su 127.0.0.1 contano per programma, non per numero; una porta nuova aperta verso l’esterno resta sempre un allarme.
fail2ban non vedeva niente
Le regole cercavano la data nella riga del log, ma fail2ban la toglie prima di confrontare. Risultato: nessun blocco, nessun errore. Le regole corrette sono state provate con righe di log vere.
Un plugin «chiuso dal 2017» che non lo era
Un noto plugin a pagamento usa lo stesso nome tecnico di un plugin gratuito chiuso anni fa su wordpress.org. Per non dare falsi allarmi, «chiuso» e «fermo» valgono solo se la versione installata è davvero un rilascio di wordpress.org, verificato con i checksum ufficiali.
Aggiornamenti falliti in silenzio
Su un sito ripristinato la cartella temporanea di WordPress puntava a quella di un altro dominio: i plugin non si scaricavano e nessuno se ne accorgeva. Ora ogni aggiornamento la controlla e la corregge da solo, e il riepilogo dice sempre cosa non si è aggiornato.
Nomi troppo lunghi
Il pacchetto .tar.gz di WordPress, aperto da wp-cli, tronca i nomi di file oltre 100 caratteri: WordPress 7 restava senza un pezzo. Si usa sempre lo zip.
Niente apostrofi, grazie
La Storage Box rifiuta le descrizioni degli snapshot con apostrofi, accenti, punti o barre. La regola ora è scritta, identica, nei tre punti della piattaforma che creano snapshot.
Una frase di questa guida che non era vera
Scrivendo «nelle cartelle scrivibili nginx non esegue PHP», la verifica sul codice ha trovato un’eccezione: la cartella dei dati di Wordfence, scrivibile e non protetta. Corretta lo stesso giorno: ora in wp-content girano solo i PHP di plugin e temi, e i controlli guardano ogni cartella scrivibile. Scrivere la documentazione è anche un modo di collaudare.
Il telefono che non era un telefono
Le prime schermate «da telefono» di questa guida erano tagliate: il browser senza interfaccia non scende sotto i 492 pixel e impaginava a quella larghezza. Rifatte con l’emulazione vera di un telefono, hanno mostrato un difetto reale della dashboard: in cinque pagine un testo per i lettori di schermo, nascosto in una cella lontana, allargava tutta la pagina fino a 742 pixel. Ora ogni tabella scorre dentro il suo riquadro, e una prova controlla tutte le pagine.
Il robot che bussa 10 volte al minuto
La pagina di accesso di WordPress accetta 10 richieste al minuto per indirizzo. Anche le prove automatiche della piattaforma ci sbattevano contro: ora usano la pagina /wp-admin/ per capire quando nginx ha ricaricato la configurazione.