Subentro forzato su un progetto PHP critico: il protocollo che applico nelle prime 72 ore quando lo sviluppatore non risponde più
Il 13 marzo 2025 ho ricevuto una telefonata alle 20:47 di un giovedì sera dal titolare di una PMI di logistica con sede a Mestre. Il suo e-commerce B2B fatturava circa 1,8 milioni di euro l'anno, gestito da un singolo sviluppatore freelance che aveva smesso di rispondere alle mail da quasi tre settimane e non rispondeva nemmeno al telefono cellulare. Quel giovedì pomeriggio l'applicazione aveva iniziato a generare errori 500 intermittenti sulla pagina di checkout, e nessuno all'interno dell'azienda sapeva dove mettere le mani. Il progetto girava su un server dedicato Hetzner AX41 di cui il cliente conosceva solo la password root scritta a mano su un foglio di carta A4 di due anni prima, il dominio era registrato presso un piccolo registrar francese tramite l'account email personale dello sviluppatore, e il codice sorgente ufficiale esisteva solo su un GitLab privato a cui soltanto il developer scomparso aveva i permessi di maintainer.
Alle 23:18 di quella sera ero collegato in SSH al server. Alle 09:12 del mattino dopo l'e-commerce era nuovamente operativo sulla pagina di checkout. Alle 17:40 del venerdì il cliente aveva riottenuto il pieno controllo amministrativo di tutti gli asset digitali. Nel weekend abbiamo iniziato a ricostruire il vero stato tecnico del progetto. Quattro settimane dopo era consolidato, documentato in modo leggibile e messo in manutenzione strutturata con un contratto di support mensile. Questa storia non è eccezionale: è esattamente il pattern che ho gestito una decina di volte in dieci anni di consulenza, e ogni volta il punto cruciale è stato lo stesso - le prime 72 ore. Il protocollo che ho consolidato funziona solo se applicato in sequenza rigida, senza saltare passaggi e senza lasciarsi prendere dal panico. Lo descrivo qui in cinque fasi, con i comandi concreti che lancio e le decisioni strategiche che ogni titolare PMI deve essere pronto a prendere quando capita "il developer scomparso".
Qual è la prima cosa da fare quando lo sviluppatore sparisce e l'applicazione genera errori 500?
La risposta controintuitiva è: non toccare il server per le prime due ore. Prima si scrive l'inventario completo degli accessi su un documento condiviso (Google Docs, Nextcloud, Notion - qualunque cosa non sia la mail personale di qualcuno), e solo dopo si apre l'SSH. Questo ribaltamento della priorità è l'errore più comune che vedo commettere: il cliente, preso dal panico, vuole "riparare subito", ma riparare senza inventario significa fare modifiche senza poter poi ricostruire cosa era rotto, come si è rotto e cosa avevi in mano all'inizio.
L'inventario minimo ha otto voci: dominio principale (registrar e account), DNS (provider e account, talvolta distinto dal registrar), server di produzione (provider, IP, credenziali), eventuale server di staging, repository del codice sorgente (GitHub/GitLab/Bitbucket), CI/CD (se esiste), email transazionale (SMTP/Mailgun/Sendgrid), e tutti gli account di servizi esterni che l'applicazione richiama (Stripe, HubSpot, marketplace, ERP esterno, gateway SMS). Il formato che uso - e che faccio compilare al cliente mentre io lavoro in parallelo - è una tabella markdown esplicita che diventa l'ancora documentale di tutto il subentro:
<!-- file: inventario-accessi.md -->
## Inventario asset digitali - subentro 2025-03-13
| Asset | Provider | Account/Login | Stato accesso | Priorità |
|--------------------|-------------------|------------------------|---------------------|----------|
| Dominio (.it) | Register.it | [email protected] | NON disponibile | P0 |
| DNS secondario | Cloudflare | [email protected] | NON disponibile | P0 |
| Server produzione | Hetzner (AX41) | root / password foglio | Disponibile | P0 |
| Repository codice | GitLab.com | devfreelance | NON disponibile | P1 |
| SMTP transazionale | Mailgun | ? | Sconosciuto | P1 |
| Stripe | stripe.com | amministrativo@azienda | Disponibile | P2 |
| HubSpot | hubspot.com | marketing@azienda | Disponibile | P3 |Questa tabella fa due cose. Primo, rende immediatamente visibili le priorità: tutto ciò che è marcato P0 e "non disponibile" è un ticket di recovery da aprire nei prossimi 60 minuti. Secondo, trasforma un'emergenza che sembra opaca in un piano a quattro righe. Hetzner, OVH e Aruba hanno tutti procedure documentate di recovery dell'accesso al pannello amministrativo tramite verifica di identità del titolare contrattuale; l'area Robot di Hetzner per il recupero del controllo amministrativo è documentata pubblicamente e normalmente funziona in 24-48 ore se hai documenti societari coerenti con l'intestazione del contratto. Lo stesso vale per OVHcloud con la procedura di cambio titolare contrattuale e per Aruba Cloud, che richiede invece un fax con visura camerale aggiornata e tempi indicativi di 3-5 giorni lavorativi.
L'errore tipico da evitare in questa fase è cercare di "fare tutto contemporaneamente". No: si compila l'inventario, si aprono i ticket di recovery in parallelo a tutti i provider P0 con i documenti societari già pronti, e poi si aspetta. Mentre si aspetta, si lavora sulle parti dove hai già accesso. La gestione di un subentro è prima di tutto un esercizio di sequenza, non di velocità.
Fase 2 - Accesso al server e prima ricognizione "a freddo"
Una volta che sei in SSH sul server (avevi già la password, oppure il provider l'ha resettata dopo la verifica del titolare), la tentazione naturale è "guardiamo subito cosa fa l'applicazione". Resisti. Il primo gesto è una ricognizione a freddo, che ti dice cosa hai davanti senza modificarlo. Il set di comandi che lancio sempre nei primi 20 minuti è questo, e va eseguito nell'ordine - ogni riga risponde a una domanda specifica che serve al comando successivo:
uname -a # kernel e architettura
cat /etc/os-release # distribuzione e versione
dpkg -l | grep -E 'php|nginx|apache|mysql|mariadb' # versioni componenti core
systemctl list-units --type=service --state=running # servizi attivi
ss -tlnp # porte in ascolto + processo
crontab -l 2>/dev/null # cron utente corrente
ls -la /etc/cron.d/ /etc/cron.*/ # cron di sistema
ls -la /var/www/ /home/ # dove vive il codice
df -h # spazio disco per partizione
free -h # memoria e swap
last -n 30 # ultimi login al serverNiente modifiche, niente restart, niente "sistemiamo subito quel warning in dmesg". Solo lettura. Questi comandi producono in mezz'ora una mappa che è già più di quanto il cliente sapesse del proprio sistema. La salvi in un file di testo nello stesso inventario condiviso - idealmente con script /tmp/recon-$(date +%s).log attivo in parallelo per catturare tutto - e diventa la baseline da cui parti per qualunque intervento successivo. È esattamente l'approccio che descrivo nella mia guida al recupero del controllo di una codebase PHP legacy senza documentazione in una PMI, qui applicato nella versione condensata da emergenza. Il principio operativo è uno solo: ogni cosa che non documenti adesso, mentre l'hai davanti, te la dimentichi nelle 48 ore di stress che seguono.
Una variante critica si presenta quando trovi versioni di PHP o MySQL palesemente vulnerabili (PHP 5.6, MySQL 5.5, kernel 3.x). In quel caso il subentro diventa anche un'operazione di sicurezza prioritaria: il rischio che il server sia già compromesso è alto, e il primo controllo tecnico aggiuntivo diventa la ricerca di indicatori di compromissione - cron job sospetti che puntano a script in /tmp, processi di mining in background, file recenti in /tmp o /var/tmp con permessi anomali, modifiche non spiegate in /etc/passwd o /etc/sudoers.d/. Se trovi indicatori di questo tipo, il flusso di lavoro cambia completamente e va orientato secondo il protocollo di incident response in 72 ore conforme NIS2 che descrivo per le PMI italiane su stack Laravel e Symfony, perché potresti avere obblighi di notifica legali oltre che tecnici.
Fase 3 - Codebase forensics: ricostruire la verità del codice
Il terzo passaggio è quello che divide un subentro fatto bene da uno improvvisato: trovare la vera versione del codice e la vera storia del repository. Quasi sempre c'è una discrepanza significativa fra ciò che gira in produzione, ciò che sta nel repository ufficiale (se esiste e ci accedi) e ciò che il developer ti aveva raccontato l'ultima volta che ci hai parlato. Le tre domande critiche sono: c'è un repository Git nella directory del progetto in produzione? Se sì, è in sync con un remote? Esistono modifiche locali non committate?
Il set di comandi forensics che lancio nella directory del progetto è il seguente, e va eseguito ancora una volta in sola lettura prima di qualunque git pull o git checkout:
cd /var/www/ecommerce
git status # modifiche non versionate
git log --oneline -30 --all # ultimi 30 commit su tutti i branch
git log --format='%ae' | sort -u # email di tutti i committer
git remote -v # remote configurati
git branch -a # branch locali e remoti
git stash list # stash dimenticati
git reflog --date=iso | head -50 # cronologia locale HEAD (anche resettati)
find . -name '*.orig' -o -name '*.bak' \
-o -name '*.swp' 2>/dev/null # file di emergenza "salvati" a manoSe git status restituisce "fatal: not a git repository", la situazione è seria ma non disperata: il codice è solo lì, su disco, senza storia, senza versioning. Va versionato adesso, immediatamente, anche solo con un primo commit etichettato come snapshot iniziale su un repository nuovo che crei tu sotto account aziendale. Nel mio articolo sull'importanza del versionamento del codice per la sopravvivenza di una PMI ho spiegato perché questo punto è non negoziabile: senza Git, ogni modifica successiva è un'operazione al buio che può solo peggiorare lo stato del sistema.
Quando invece trovi modifiche locali non committate - succede nell'80% dei subentri che faccio - la sequenza da applicare è precisa. Prima si cattura lo stato attuale in un branch dedicato, poi si fa push su un repository sotto il tuo controllo (non quello del developer scomparso), e solo dopo si inizia a ragionare sui fix:
cd /var/www/ecommerce
git checkout -b subentro/snapshot-2025-03-13
git add -A
git commit -m "snapshot: stato produzione al subentro 2025-03-13" \
--author="Maurizio Fonte <[email protected]>"
git remote add subentro https://github.com/cliente/ecommerce-subentro.git
git push -u subentro subentro/snapshot-2025-03-13Da quel momento hai una baseline tracciabile. Tutto quello che farai dopo sarà incrementale, reversibile e auditabile. Se invece il developer aveva commit recenti su un remote a cui non hai accesso, devi parallelamente avviare il recovery di quell'account: GitHub, GitLab e Bitbucket hanno tutti procedure documentate di transfer di ownership che tipicamente richiedono 5-10 giorni lavorativi a patto di esibire documenti societari coerenti con l'intestazione dell'account organization.
Fase 4 - Stop-the-bleeding: mai grandi modifiche, solo i due-tre fix critici
Se il sito è andato giù, il cliente vuole vederlo tornare online. È umano e legittimo. Ma il principio guida di questa fase è uno solo: fai il minimo indispensabile per fermare l'emorragia, e niente di più. Questa è la fase in cui un consulente meno esperto fa danni perché "già che ci siamo sistemiamo anche quell'altra cosa". No. La logica da applicare è quella del triage di pronto soccorso: fermi il sangue, stabilizzi il paziente, e poi, dopo, a cliente stabilizzato, fai la diagnosi completa e pianifichi gli interventi strutturali.
Le modifiche tipiche che faccio in questa fase sono due o tre, mai di più. Una correzione mirata al bug che ha causato il fermo - e nella mia esperienza il 70% dei "site down" da subentro rientra in uno di questi quattro casi: un cron rotto, una directory di cache o log piena al 100%, una credenziale di servizio esterno scaduta, un certificato SSL non rinnovato. Un commit esplicito della modifica nel branch nuovo di subentro. Un riavvio chirurgico del solo servizio coinvolto (systemctl restart php8.2-fpm, non reboot). Niente upgrade di pacchetti apt, niente modifiche alla configurazione di nginx o Apache, niente "approfittiamo per spostare il sito su un nuovo server con PHP aggiornato". Tutto questo verrà dopo, quando avrai capito davvero cosa hai in mano e avrai piani di rollback testati. Il protocollo che applico in questa fase è una variante semplificata di quello che descrivo nella mia guida pratica al refactoring di codice PHP legacy in produzione: incrementale, reversibile, sempre con piano di rollback scritto prima di applicare la modifica.
C'è un caso particolare che merita una menzione separata. Se durante la Fase 4 scopri che il fermo non è dovuto a un bug ordinario ma a una compromissione (defacement, ransomware, dati esfiltrati, backdoor attive), il subentro tecnico si sovrappone a un data breach con obblighi legali propri. In questo caso scattano gli obblighi di notifica al Garante per la protezione dei dati personali entro 72 ore secondo l'articolo 33 del GDPR, il cui testo completo è consultabile su EUR-Lex, e il cliente deve essere messo al corrente immediatamente: la decisione di notificare o meno spetta a lui in quanto titolare del trattamento, non a te in quanto consulente tecnico. Il tuo compito è documentare fatti e tempistiche in modo che la decisione venga presa con tutte le informazioni disponibili.
Fase 5 - Dall'emergenza al consolidamento: il piano dei 30 giorni successivi
Una volta che il sito è stabile, gli accessi sono recuperati, il codice è versionato e sotto il tuo controllo, esci ufficialmente dalla fase di emergenza ed entri nella fase di consolidamento. È qui che hai finalmente il tempo (relativo) di fare le cose nel modo giusto: documentazione completa dell'infrastruttura, hardening di sicurezza secondo le linee guida che descrivo nella mia guida alla compliance NIS2 per server Hetzner e OVH, pianificazione di backup verificati con restore test reale e non solo nominale, definizione di una routine di patching mensile con finestra di manutenzione concordata, e pianificazione del refactoring del debito tecnico ereditato. Quest'ultimo punto è esattamente quello che ho approfondito nel mio articolo dedicato alla gestione del debito tecnico su server Linux nei 90 giorni post-subentro: è un lavoro di tre-sei mesi che inizia subito ma non finisce mai veramente, perché il debito tecnico è come il peso corporeo - non lo azzeri, lo gestisci.
Il punto culturale che chiudo sempre con i clienti dopo un subentro di emergenza è questo: il "developer scomparso" non è un evento eccezionale. È un rischio strutturale che ogni PMI con dipendenza da un singolo fornitore tecnico ha sempre, anche se non se ne rende conto finché non accade. Il framework ENISA di gestione del rischio cyber per le PMI lo classifica esplicitamente come "single point of failure umano" ed è una delle voci più frequenti nelle valutazioni del rischio operativo che faccio per i clienti nei primi 30 giorni. La risposta strutturale è una sola e non ammette scorciatoie: documentazione viva (aggiornata mensilmente, non una tantum), accessi multipli intestati al titolare per ogni asset digitale, repository sotto il controllo aziendale e non personale, contratti di manutenzione che prevedano handover formale in caso di interruzione del rapporto. Tutte cose che si fanno prima dell'emergenza, perché farle durante costa dieci volte di più ed espone il business al rischio di non riuscire a completarle affatto.
Se gestisci una PMI e ti riconosci in questo scenario - un progetto critico in mano a un solo sviluppatore di cui sai poco e che potrebbe sparire domani mattina senza preavviso - non aspettare il giovedì sera della telefonata di emergenza. Scopri come lavoro con i clienti sul tema della sovranità tecnica e del rischio "developer scomparso": in dieci anni di consulenza ho gestito una decina di subentri di emergenza e una trentina di audit di rischio strutturale preventivi, e i secondi sono sistematicamente costati ai clienti un decimo dei primi. Se invece sei già nel pieno dell'emergenza e hai bisogno di un consulente che applichi sul tuo progetto il protocollo di 72 ore che ho appena descritto, contattami per una consulenza: in caso di emergenze critiche posso essere operativo entro poche ore con un piano d'azione strutturato che riprende il controllo del tuo progetto PHP, indipendentemente dal provider (Hetzner, OVH, Aruba, DigitalOcean) e dallo stato iniziale della codebase.