Architettura cybersecurity per PMI: cinque principi che spiego a ogni nuovo cliente (e uno che uccide chi ci crede)
Nel settembre 2024 ho audited un e-commerce B2B veneto che aveva subito una compromissione: un attaccante era entrato via un WordPress marketing satellite (non il core Laravel dell'e-commerce) e si era mosso lateralmente fino al MySQL dell'e-commerce, esfiltrando circa 12.000 record cliente. Quando ho presentato il report all'IT manager, la sua prima frase è stata: "Ma avevamo il firewall, l'antivirus, le password complesse, e il database era separato". Aveva ragione sulla carta. Nella realtà, tutte e quattro le difese appartenevano allo stesso livello logico - erano tutte "autenticazione + crittografia di base" - e nessuna copriva il vettore effettivo: escalation via account di servizio WordPress con privilegi a DB condivisi. Quello che mancava non erano i principi, erano le applicazioni concrete.
Questo articolo raccoglie i cinque principi che illustro a ogni nuovo cliente PMI nelle prime due ore di audit. Sono principi vecchi di cinquant'anni, che tutti dicono di conoscere - eppure il gap non è culturale, è operativo. Conoscere "defense in depth" non è lo stesso che avere davvero difese eterogenee su livelli diversi.
Defense in depth: il numero di livelli non conta, conta la loro eterogeneità
Il principio è vecchio di secoli: non affidare la tua sicurezza a un singolo punto di difesa. L'applicazione che vedo più spesso in audit è sbagliata in un modo sottile. I clienti pensano che "defense in depth" significhi "avere molti controlli di sicurezza", e poi ne mettono cinque o sei che sono tutti dello stesso tipo: firewall perimetrale, firewall applicativo, ACL di rete, ACL applicativa, ACL di database. Cinque livelli, certo, ma un attaccante che sfonda il primo layer perché trova una credenziale valida - scenario realistico nel 2026 - ha in mano una chiave che apre tutti gli altri, perché sono tutti basati sulla stessa primitiva di sicurezza (identità valida = accesso autorizzato).
Defense in depth vera è quella descritta nel NIST SP 800-207 sulla Zero Trust Architecture: ogni livello deve essere di un tipo diverso e deve fallire in modo diverso. In uno stack Laravel su server Debian/Ubuntu, uno schema che funziona è:
- Livello 1 - Network boundary: WireGuard o Tailscale come accesso unico per la management plane, niente SSH esposto su porta pubblica. Un attaccante con credenziali applicative non passa questo livello perché non ha la chiave WireGuard del device.
- Livello 2 - Application authentication: Laravel auth con MFA obbligatorio per tutti gli account amministrativi, WebAuthn/passkey dove possibile.
- Livello 3 - Rate limiting e behavioral detection: throttling su endpoint sensibili, alert su pattern anomali (login fuori orario, geolocation inconsistente, velocità di richiesta fuori norma).
- Livello 4 - Data layer isolation: il DB accetta connessioni solo dal socket locale dell'app server o da VPC privato, con utenti applicativi dal privilegio minimo (nessun
GRANT ALL), e i dati sensibili sono crittografati a livello colonna con una chiave gestita da un key-management service esterno. - Livello 5 - Monitoring e detection: log aggregati in una sink fuori dal server compromettibile (Loki, Graylog, cloud), con alert su pattern specifici (query DB inusuali, accessi a tabelle sensibili, errori 500 in sequenza).
Ognuno di questi livelli fallisce per motivi diversi. Un attaccante che bypassa il livello 2 (credenziali rubate) deve comunque passare il livello 1 (WireGuard), che non si sblocca con un password leak. Un attaccante che passa livello 1 e 2 incappa nei detection del livello 5 prima di esfiltrare tutto. Questo è il significato vero di "in profondità": eterogeneità, non ridondanza. La mia checklist di hardening NIS2-ready per Laravel e Symfony è esplicitamente strutturata su layer di tipi diversi per questa ragione - è un artefatto deliberato, non una collezione casuale di raccomandazioni.
Least privilege: il privilege creep è il killer, la revoca è più importante della concessione
Il principio del minimo privilegio è facile da spiegare e difficile da applicare nel lungo periodo. Il motivo non è che i team non sappiano concedere i permessi corretti al momento dell'onboarding - di solito lo sanno fare. Il motivo è che nessuno revoca i permessi quando non servono più, e nel tempo ogni utente accumula autorizzazioni come un sedimento geologico. Il termine tecnico è privilege creep, ed è il modo in cui il principio del least privilege muore silenziosamente in tutte le PMI che non hanno un processo esplicito di review.
Un audit che faccio sempre nei primi 30 giorni è il permission cliff report: elenco tutti gli accessi a GitHub/GitLab, cloud, VPN, database, hosting; per ciascuno mappo quando sono stati usati l'ultima volta. Trovo sistematicamente che il 30-50% degli accessi non è stato usato negli ultimi 90 giorni. In un cliente nel 2023 ho trovato un ex-dipendente con SSH key ancora attiva su 14 server in produzione, 8 mesi dopo la sua uscita; l'HR aveva fatto l'offboarding "amministrativo" ma nessuno aveva rimosso le chiavi operative.
Il pattern che propongo ai clienti è semplice e si compone di tre regole:
- Ogni concessione di privilegio ha un timestamp di scadenza. Non "per sempre, finché non si decide di revocare". Un trimestre, sei mesi, un anno - ma una scadenza esplicita deve esistere. Al momento della scadenza, si riesamina: se serve ancora, si rinnova; se no, si revoca. Strumenti come Teleport, HashiCorp Boundary o le temporary credentials di AWS IAM lo fanno nativamente.
- L'onboarding e l'offboarding sono checklist identiche, al contrario. Ogni permesso concesso all'onboarding deve comparire nella checklist di offboarding. Se manca, l'offboarding non è completo. Questo sembra banale ma in una PMI dove l'IT è "quel ragazzo che fa anche altro" è raramente applicato.
- Gli account di servizio hanno lo stesso trattamento degli account umani. Anzi, peggio: gli account di servizio con password statiche e permessi ampi sono il principale vettore di lateral movement che ho visto in incident response. L'account di servizio che muove dati tra sistemi non deve avere permessi di modifica se la sua funzione è solo lettura.
Questa disciplina di revoca è particolarmente critica quando la PMI adotta frameworks di compliance come NIS2, dove l'articolo 21 della direttiva NIS2 richiede esplicitamente "politiche di controllo degli accessi", ed è ripresa anche dai CIS Critical Security Controls v8 nella categoria Access Control Management. Un auditor che vedrà il tuo permission cliff report come documento vivo ti darà la spunta; un auditor che vedrà una password-policy.pdf del 2019 non ti darà niente. Su come tradurre concretamente gli obblighi NIS2 per server Hetzner/OVH/Aruba, vedi la mia guida operativa alla compliance NIS2.
Separation of duties nelle PMI di 3 persone: il compromesso onesto
Il principio classico dice: nessuna persona da sola deve poter completare un'azione critica. In una banca questo significa che chi immette un bonifico non è chi lo approva. In una PMI italiana con 3 sviluppatori e mezzo IT manager, questo principio si scontra con un vincolo insormontabile: non hai abbastanza persone per "separare" davvero i compiti. Dire "chi scrive codice non deve fare deploy" a un team di 3 persone significa che nessuno fa deploy, oppure qualcuno viola il principio ogni giorno.
Il compromesso onesto che propongo ai miei clienti piccoli è sostituire "separation of duties umana" con "separation of duties automatizzata tramite pipeline". Il team fa tutto, ma il sistema fa i check. Concretamente:
- Il deploy in produzione è automatico via pipeline CI/CD, non manuale. Nessuno "entra in SSH su prod e fa
git pull". Il deploy parte solo quando un tag è stato firmato e la pipeline ha superato tutti i gate (test, SAST, dependency review, smoke test in staging). - I commit su main sono protetti: branch protection rule con "require 1 approval", anche se l'approvatore è uno dei 3 sviluppatori. L'approvatore non può essere l'autore del commit. In un team di 3 persone questo crea rotazione naturale dei reviewer.
- Gli accessi amministrativi al DB richiedono un secondo fattore gestito fuori dal sistema stesso: un 1Password condiviso con audit log, oppure un bastion con record della sessione (Teleport/Boundary). Così, anche se una persona ha tecnicamente accesso, l'azione è registrata e visibile.
- Il segreto di produzione non è nell'environment dello sviluppatore. Mai. Stage e prod usano secret diversi, gestiti da un vault, e lo sviluppatore usa solo lo stage. Per fare una modifica al prod deve richiedere un'elevation temporanea, che lascia una traccia.
Questo pattern sposta il principio da "duties separate fra persone" a "duties separate fra persona + sistema". Non è l'implementazione pura del principio teorico, ma è l'implementazione realistica per un team di 3 persone, e il pattern di pipeline-as-gate è quello che chiamo DevSecOps operativo: la pipeline diventa il "secondo paio di occhi" che l'azienda non può permettersi di avere umani.
Secure by design + KISS: perché la complessità genera alert fatigue che uccide la difesa
Accorpo questi due principi perché nella mia esperienza vanno sempre insieme: "secure by design" significa decidere la postura di sicurezza all'inizio del progetto invece di applicarla come toppa alla fine; "KISS" significa che quella postura deve essere realisticamente gestibile dal team che dovrà viverci. Se violi il secondo, in sei mesi violi anche il primo, perché il team costruisce bypass per sopravvivere alla complessità.
Ho fatto questo errore in prima persona nel 2020 su un cliente. Volevo essere molto rigoroso e ho configurato un WAF (ModSecurity con OWASP Core Rule Set ispirato agli attacchi catalogati nel MITRE ATT&CK Framework) molto aggressivo, un SIEM che generava alert su tutto, e una policy di log review quotidiana. Dopo tre settimane il team del cliente aveva messo il WAF in "detect only" (cioè praticamente off) perché generava centinaia di falsi positivi al giorno su endpoint legittimi dell'e-commerce, il SIEM era stato silenziato perché gli alert arrivavano ogni 10 minuti e nessuno li leggeva più, e la "log review quotidiana" era diventata un job cron che scartava tutto. Avevo creato tre layer di sicurezza che sulla carta sembravano impeccabili, e nella pratica erano disattivati tutti e tre. Era colpa mia, non del team: avevo violato KISS e di conseguenza secure by design era morto con lui.
La lezione che ho estratto è: ogni controllo di sicurezza aggiuntivo deve superare un cost/benefit test esplicito. Il costo è misurato in termini di: (a) falsi positivi che il team dovrà triagiare ogni settimana, (b) tempo di setup iniziale, (c) overhead operativo mensile. Il beneficio è misurato in: (a) classi di attacco che il controllo blocca, (b) riduzione del tempo medio di detection. Se il costo supera il beneficio, il controllo viene rimosso, non "migliorato". Un controllo che genera più di 5 falsi positivi a settimana è già border-line; uno che ne genera più di 20 viene disattivato in automatico dal team nel giro di un mese, indipendentemente da cosa dice la policy. Questa è anche la lezione operativa che passo ai clienti quando devono pianificare un incident response NIS2-ready su stack Laravel/Symfony: se il tuo team è già saturo di alert falsi, un alert vero quella notte non lo vede nessuno.
Secure by design vero, in una PMI, significa scegliere meno controlli ma applicati meglio. Un WAF con regole custom scritte per il tuo specifico stack Laravel batte sempre un WAF generico con OWASP CRS in "paranoid mode 3". Un alert ogni 24 ore che richiede azione vera batte sempre 200 alert al giorno che il team ignora. Questa è l'intersezione di KISS e secure by design: sicurezza sostenibile nel tempo, non sicurezza massimizzata a giorno zero. Il pezzo complementare sull'integrare questi gate direttamente in CI è trattato nel mio playbook supply chain security per Laravel e Symfony.
Il principio da non seguire mai: security by obscurity travestita da "hardening"
Eccoci al principio che continua a uccidere PMI che pensavano di essere furbe: security by obscurity. Il principio di Kerckhoffs del 1883 dice che un sistema crittografico deve rimanere sicuro anche se tutto di esso è pubblico, tranne la chiave. La versione applicata alla sicurezza sistemistica è: una difesa che funziona solo finché l'attaccante non conosce un dettaglio "segreto" non è una difesa, è una scommessa sul tempo che passerà prima che quel dettaglio venga scoperto.
Il caso che vedo più spesso in audit è il rituale della porta SSH spostata: "abbiamo messo SSH sulla 22022 invece della 22, così gli attacchi automatici non ci trovano". Questa è security by obscurity pura. Uno scan con masscan o zmap sull'intero IP pubblico di un cliente, tipo un /28, richiede circa 3-5 secondi per enumerare tutte le porte aperte su tutti gli IP, incluso un SSH sulla 22022. Il "nascondersi" dura esattamente il tempo del prossimo scan Shodan, cioè giorni. Nel frattempo, hai creato un falso senso di sicurezza che ha spinto il team a deprioritizzare le vere mitigation - disabilitare password auth, limitare AllowUsers, fail2ban, e soprattutto mettere SSH dietro WireGuard o bastion.
Altri pattern che continuano a comparire: "abbiamo un CMS custom quindi siamo al sicuro" (un CMS custom è solo un CMS che nessuno ha ancora audited, con superficie di attacco probabilmente peggiore di WordPress), "il nostro API è su un sottodominio non linkato" (i certificate transparency log su crt.sh lo indicizzano entro 24 ore dall'emissione del certificato SSL), "il database ha password random lunghissima quindi è sicuro anche se esposto" (Kerckhoffs dice che la password deve essere l'unica linea di difesa per scelta, non per ripiego perché non hai segmentato la rete).
La differenza pratica fra sicurezza vera e security by obscurity è questa domanda: se pubblicassi tutti i dettagli della tua configurazione domani su Twitter, il sistema rimarrebbe sicuro? Se la risposta è no, stai facendo security by obscurity. Se la risposta è sì - perché la sicurezza si regge su chiavi robuste, crittografia pubblica, accessi autenticati, segmentazione di rete verificata, e non su "nessuno lo sa" - allora stai applicando sicurezza vera.
Questi cinque principi più l'anti-principio non sono un mantra da recitare: sono una lista di domande operative che faccio a ogni nuovo cliente PMI nelle prime due ore del kickoff di un audit. "Dove sono i layer eterogenei di defense in depth? Chi ha quale privilegio e quando l'ha usato l'ultima volta? Come separate le duties quando siete in tre? Ogni controllo di sicurezza è sostenibile o è stato già silenziato di fatto? Quale parte della vostra sicurezza morirebbe se un attaccante conoscesse i vostri secret non crittografici?". Se il cliente risponde "non lo so" a una delle cinque, quella è la priorità uno del piano di lavoro. Se vuoi capire come questi principi si traducono in un piano concreto per la tua PMI, scopri come lavoro con i clienti sui temi di architettura di sicurezza: dieci anni di audit e incident response su stack PHP/Laravel mi hanno convinto che il gap fra "conoscere i principi" e "applicarli" è il singolo predittore più affidabile di quali PMI saranno in prima pagina sui giornali per un data breach nei prossimi 24 mesi. Se vuoi un assessment concreto della postura di sicurezza della tua infrastruttura con un piano di lavoro su 90 giorni basato su questi cinque principi, contattami per una consulenza: in due settimane di audit ti consegno permission cliff report, mappa dei layer di difesa eterogenei, checklist di separation of duties automatizzata, e lista prioritizzata dei controlli che stanno già generando alert fatigue nel tuo team.