Ogni mattina il team di sicurezza di una media impresa italiana apre la stessa scheda del browser: il catalogo CISA Known Exploited Vulnerabilities (KEV). Cerca a occhio le voci nuove, controlla se riguardano i prodotti in uso, poi le segnala su Teams o Slack. Il problema è che questo processo manuale funziona bene finché il catalogo cresce di due o tre righe al mese. Nel 2026 il KEV supera le 1.500 voci e continua a crescere quasi ogni settimana, e leggerlo a mano diventa un modo sicuro per perdere proprio la vulnerabilità che conta.
In questa guida costruiamo da zero uno script in Python 3.12 che scarica il catalogo CISA KEV, individua le nuove voci rispetto al giorno prima, filtra quelle rilevanti per lo stack software europeo e le invia a un SIEM aziendale tramite API REST. Non serve licenza commerciale, non serve un tool specifico: bastano poche librerie standard e uno scheduler. Alla fine avrai un progetto funzionante che puoi adattare al tuo ambiente e programmare come job periodico.
Perché costruirsi uno script invece di comprare un tool
La prima domanda che si pone chi gestisce un budget IT limitato è semplice: perché scrivere uno script invece di acquistare una piattaforma di threat intelligence già pronta? La risposta ha due parti. La prima è il costo: molte piattaforme di vulnerability intelligence commerciali partono da diverse migliaia di euro l’anno per funzionalità che, nel caso specifico del KEV, si riducono a leggere un file JSON pubblico e confrontarlo con lo stato precedente. La seconda è il controllo: uno script scritto in casa si integra esattamente con il SIEM, il formato di log e il playbook che l’azienda già usa, senza dover adattare i propri processi al formato imposto da un fornitore esterno.
Questo non significa che gli strumenti commerciali siano inutili: per organizzazioni con centinaia di asset e un team SOC dedicato, una piattaforma con dashboard, arricchimento automatico e correlazione multi-fonte resta spesso la scelta più efficiente. Ma per una piccola o media impresa italiana che deve solo intercettare in modo affidabile le CVE del KEV rilevanti per il proprio parco software, uno script Python di poche centinaia di righe, gratuito e completamente sotto il proprio controllo, è spesso la soluzione più pragmatica per iniziare. Ed è anche un buon punto di partenza tecnico: una volta capito il flusso dati end-to-end, diventa più facile valutare con cognizione di causa se e quando serva davvero passare a un prodotto commerciale.
Cos’è il catalogo CISA KEV e perché conta per la sicurezza aziendale
Il National Vulnerability Database (NVD) assegna ogni giorno decine di nuovi identificativi CVE, ma non tutte le vulnerabilità pubblicate vengono davvero sfruttate in attacchi reali. Il catalogo Known Exploited Vulnerabilities, mantenuto dalla Cybersecurity and Infrastructure Security Agency statunitense, restringe il campo: contiene solo le CVE che risultano attivamente sfruttate, con vendor, prodotto interessato, data di aggiunta, scadenza per la remediation (due date) e un flag che indica se la falla è nota per essere usata in campagne ransomware.
Il file è pubblicato in formato CSV e JSON e pensato apposta per l’automazione: nessuna autenticazione richiesta, nessun rate limit aggressivo, solo un endpoint statico che si aggiorna quando CISA pubblica nuove voci. Le linee guida ufficiali indicano queste vulnerabilità come priorità assoluta per la remediation, prima ancora di guardare al punteggio CVSS. Una CVE con punteggio 6.5 ma presente nel KEV va patchata prima di una CVE con punteggio 9.8 che non risulta sfruttata: è il criterio “known exploited” a determinare l’urgenza operativa, non solo la gravità teorica.
Per un’azienda italiana soggetta a NIS2 questo catalogo è anche un punto di riferimento pratico: dimostra, in caso di ispezione ACN, che l’organizzazione monitora attivamente le vulnerabilità note e le priorizza in base allo sfruttamento reale, non solo su un foglio Excel aggiornato una volta al trimestre.
Prerequisiti: cosa serve prima di iniziare
Prima di scrivere una sola riga di codice, prepara l’ambiente. Ecco l’elenco completo con le versioni testate per questa guida:
| Componente | Versione consigliata | Note |
|---|---|---|
| Python | 3.12.x | Usa python3 --version per verificare |
| requests | 2.32.x | Client HTTP per scaricare il feed KEV |
| pandas | 2.2.x | Parsing e confronto del CSV/JSON |
| schedule | 1.2.x | Esecuzione periodica senza cron esterno |
| Sistema operativo | Linux, macOS o Windows con WSL2 | Gli esempi usano percorsi Unix |
| Accesso SIEM | Endpoint REST con token API | Va bene qualunque SIEM che accetti eventi JSON via POST |
| Permessi di rete | Uscita HTTPS verso cisa.gov | Verifica che il proxy aziendale non blocchi il dominio |
Non serve una VM dedicata: uno script Python di poche decine di righe, eseguito su un piccolo server o anche su un Raspberry Pi in rete aziendale, è sufficiente per questo caso d’uso. Verifica anche di avere accesso in scrittura alla directory dove salverai lo snapshot e i log: su server condivisi con permessi restrittivi, questo è un dettaglio che blocca l’esecuzione al primo tentativo se non viene controllato prima.
Se lavori già con librerie Python per altri progetti di sicurezza, come lo client requests usato anche per interrogare API interne, il salto di apprendimento per questa guida è minimo: le stesse competenze si riusano quasi interamente. Chi parte invece da zero con Python può comunque seguire la guida passo per passo, perché ogni blocco di codice viene spiegato singolarmente prima di essere assemblato nello script finale al passo 8.
Architettura del sistema in sintesi
Prima di scrivere codice, vale la pena avere chiaro il flusso dati completo, perché ogni passo della guida costruisce un pezzo di questa catena. Lo script scarica il feed JSON da CISA (passo 2), lo confronta con lo snapshot salvato dall’esecuzione precedente per isolare solo le voci nuove (passo 3), applica un filtro sui vendor effettivamente presenti nel parco IT (passo 4), assegna una priorità in base al flag ransomware e alla scadenza (passo 5), costruisce un payload JSON standardizzato (passo 6) e infine lo invia al SIEM tramite una chiamata REST autenticata (passo 7). Ogni fase è indipendente e testabile da sola, il che significa che se un giorno cambi SIEM ti basta riscrivere la funzione di invio, senza toccare la logica di download, confronto e prioritizzazione.
Questa separazione in funzioni distinte, piuttosto che un unico blocco di codice monolitico, è anche ciò che rende possibile scrivere test automatici mirati, come vedremo nel passo 12. Ogni funzione riceve un input chiaro e restituisce un output prevedibile, così puoi verificarne il comportamento isolatamente senza dover scaricare davvero il catalogo KEV a ogni test.
Passo 1: crea l’ambiente virtuale Python
Isola le dipendenze del progetto in un ambiente virtuale, così non rischi conflitti con altri script già presenti sul server.
mkdir kev-monitor && cd kev-monitor
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install requests pandas schedule
Su Windows, l’attivazione dell’ambiente virtuale cambia in venv\Scripts\activate. Verifica sempre che il prompt del terminale mostri il prefisso (venv) prima di procedere: è il segnale che le librerie installate restano isolate dal Python di sistema.
Passo 2: scarica e analizza il catalogo KEV in JSON
Il feed ufficiale JSON è pubblicato all’indirizzo known_exploited_vulnerabilities.json. Ogni voce contiene i campi cveID, vendorProject, product, vulnerabilityName, dateAdded, dueDate, knownRansomwareCampaignUse e una breve descrizione. Scriviamo una prima funzione che lo scarica e lo carica in un DataFrame pandas.
import requests
import pandas as pd
KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
def scarica_kev():
risposta = requests.get(KEV_URL, timeout=15)
risposta.raise_for_status()
dati = risposta.json()
df = pd.DataFrame(dati["vulnerabilities"])
df["dateAdded"] = pd.to_datetime(df["dateAdded"])
df["dueDate"] = pd.to_datetime(df["dueDate"])
return df
if __name__ == "__main__":
catalogo = scarica_kev()
print(f"Voci totali nel catalogo: {len(catalogo)}")
print(catalogo[["cveID", "vendorProject", "product", "dateAdded"]].tail(5))
Esegui lo script con python download_kev.py. Se la connessione funziona, l’output mostra il numero totale di voci e le ultime cinque aggiunte per data.
Output atteso del primo script
Voci totali nel catalogo: 1547
cveID vendorProject product dateAdded
1542 CVE-2026-41102 Ivanti Connect Secure 2026-08-21
1543 CVE-2026-40987 Fortinet FortiOS 2026-08-22
1544 CVE-2026-39871 Microsoft Windows IKE 2026-08-24
1545 CVE-2026-38220 Adobe ColdFusion 2026-08-26
1546 CVE-2026-37655 Oracle WebLogic Server 2026-08-27
I numeri di CVE mostrati qui sono di esempio: il tuo output rifletterà le voci realmente presenti nel catalogo al momento dell’esecuzione, che cambia in continuazione.
Passo 3: salva uno snapshot locale per il confronto giornaliero
Per capire cosa è nuovo rispetto al giorno prima, serve un punto di riferimento. Salviamo ogni esecuzione in un file locale e confrontiamo il CVE ID con lo snapshot precedente.
import os
import pandas as pd
SNAPSHOT_PATH = "kev_snapshot.csv"
def carica_snapshot_precedente():
if os.path.exists(SNAPSHOT_PATH):
return pd.read_csv(SNAPSHOT_PATH)
return pd.DataFrame(columns=["cveID"])
def salva_snapshot(df):
df.to_csv(SNAPSHOT_PATH, index=False)
def trova_nuove_voci(catalogo_attuale, snapshot_precedente):
cve_gia_visti = set(snapshot_precedente["cveID"])
nuove = catalogo_attuale[~catalogo_attuale["cveID"].isin(cve_gia_visti)]
return nuove
Alla prima esecuzione, lo snapshot è vuoto e quindi ogni voce del catalogo risulterà “nuova”. È normale: dalla seconda esecuzione in poi, lo script segnalerà solo le CVE aggiunte da CISA dopo l’ultimo controllo.
Passo 4: filtra per vendor e prodotti rilevanti per la tua azienda
Un’azienda con 200 dipendenti non usa tutti i prodotti del mondo. Ha senso restringere gli alert a una lista di vendor effettivamente presenti nel parco IT: firewall, VPN, sistemi operativi, CMS, database. Mantieni questa lista in un file di configurazione separato, così puoi aggiornarla senza toccare la logica dello script.
# config_vendor.py
VENDOR_RILEVANTI = [
"Microsoft", "Fortinet", "Ivanti", "Cisco",
"VMware", "Oracle", "Adobe", "Apache",
"SonicWall", "Citrix", "Atlassian"
]
def filtra_per_vendor(df, vendor_lista):
maschera = df["vendorProject"].str.contains(
"|".join(vendor_lista), case=False, na=False
)
return df[maschera]
Nota che la lista va rivista almeno ogni trimestre: se l’azienda cambia fornitore di firewall o migra da un CMS a un altro, il filtro deve seguire l’inventario reale, non restare congelato alla configurazione del giorno del deploy.
Passo 5: dai priorità alle CVE legate a campagne ransomware
Il campo knownRansomwareCampaignUse del catalogo KEV indica se quella specifica vulnerabilità è nota per essere sfruttata in campagne ransomware attive. È il segnale più forte che il catalogo fornisce: una CVE con questo flag a “Known” merita una risposta nelle ore, non nei giorni.
def calcola_priorita(riga):
if riga["knownRansomwareCampaignUse"] == "Known":
return "CRITICA"
giorni_alla_scadenza = (riga["dueDate"] - pd.Timestamp.now()).days
if giorni_alla_scadenza <= 7:
return "ALTA"
return "MEDIA"
def assegna_priorita(df):
df = df.copy()
df["priorita"] = df.apply(calcola_priorita, axis=1)
return df.sort_values("priorita")
Questa classificazione a tre livelli (CRITICA, ALTA, MEDIA) è volutamente semplice: nella pratica funziona meglio di una scala a dieci gradini perché il team di guardia deve decidere in pochi secondi se aprire un ticket urgente o metterlo in coda.
Passo 6: costruisci il payload JSON per il SIEM
Ogni SIEM ha un formato di ingestion leggermente diverso, ma quasi tutti accettano eventi JSON via API REST con un timestamp, una categoria e un corpo di dettaglio. Costruiamo un payload generico che puoi adattare al mapping campi del tuo prodotto.
from datetime import datetime, timezone
def costruisci_evento(riga):
return {
"timestamp": datetime.now(timezone.utc).isoformat(),
"event_type": "cisa_kev_alert",
"cve_id": riga["cveID"],
"vendor": riga["vendorProject"],
"product": riga["product"],
"vulnerability_name": riga["vulnerabilityName"],
"date_added": str(riga["dateAdded"].date()),
"due_date": str(riga["dueDate"].date()),
"ransomware_use": riga["knownRansomwareCampaignUse"],
"priority": riga["priorita"],
"source": "cisa-kev-catalog"
}
Passo 7: invia gli eventi al SIEM via API REST
Con il payload pronto, l'invio è una semplice richiesta POST autenticata con un token bearer. Gestisci sempre gli errori di rete: un SIEM temporaneamente irraggiungibile non deve far fallire l'intero script.
import os
SIEM_ENDPOINT = os.environ.get("SIEM_ENDPOINT", "https://siem.example-azienda.it/api/events")
SIEM_TOKEN = os.environ.get("SIEM_TOKEN")
def invia_al_siem(evento):
headers = {
"Authorization": f"Bearer {SIEM_TOKEN}",
"Content-Type": "application/json"
}
try:
risposta = requests.post(SIEM_ENDPOINT, json=evento, headers=headers, timeout=10)
risposta.raise_for_status()
return True
except requests.exceptions.RequestException as errore:
print(f"Errore invio evento {evento['cve_id']}: {errore}")
return False
Non scrivere mai il token direttamente nello script. Usa variabili d'ambiente, come mostrato sopra, oppure un secret manager se l'azienda ne ha già uno in produzione. Un token SIEM che finisce in un repository Git pubblico è un incidente evitabile al cento per cento.
Mettere in sicurezza lo script stesso
C'è un paradosso che vale la pena affrontare esplicitamente: uno strumento di monitoraggio della sicurezza che non è a sua volta sicuro rischia di diventare un nuovo punto debole. Il token del SIEM, se compromesso, permette a un attaccante di iniettare eventi falsi o di scoprire quali vulnerabilità l'azienda considera prioritarie, informazione che aiuta a pianificare un attacco mirato. Per questo motivo, oltre a evitare il token in chiaro nel codice, conviene limitare i permessi del token stesso al solo endpoint di ingestion eventi, senza concedergli accesso in lettura ad altre parti del SIEM.
Un secondo accorgimento riguarda il file di snapshot: contiene solo CVE ID, quindi non è un dato sensibile in sé, ma va comunque protetto da scritture non autorizzate. Se un attaccante con accesso al server riuscisse a manipolarlo, potrebbe far sparire silenziosamente alcune CVE dal confronto, impedendo allo script di segnalarle come nuove. Imposta permessi restrittivi sul file (chmod 600 su Linux) e, se possibile, esegui lo script con un utente dedicato a privilegi minimi, non con l'utente root o l'amministratore di sistema.
Infine, valida sempre la risposta HTTP del feed CISA prima di elaborarla: un feed manomesso da un attacco man-in-the-middle su una rete non protetta da HTTPS valido potrebbe restituire dati falsati. La libreria requests verifica i certificati TLS di default, quindi evita di disattivare questa verifica con verify=False anche per test rapidi: è un'abitudine che spesso sopravvive per errore fino alla produzione.
Passo 8: unisci tutto in un unico script eseguibile
Ora mettiamo insieme i pezzi precedenti in un file unico, kev_monitor.py, che rappresenta la versione funzionante completa del progetto.
import os
import requests
import pandas as pd
from datetime import datetime, timezone
KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
SNAPSHOT_PATH = "kev_snapshot.csv"
VENDOR_RILEVANTI = ["Microsoft", "Fortinet", "Ivanti", "Cisco", "VMware", "Oracle", "Adobe", "Apache"]
SIEM_ENDPOINT = os.environ.get("SIEM_ENDPOINT")
SIEM_TOKEN = os.environ.get("SIEM_TOKEN")
def scarica_kev():
r = requests.get(KEV_URL, timeout=15)
r.raise_for_status()
df = pd.DataFrame(r.json()["vulnerabilities"])
df["dateAdded"] = pd.to_datetime(df["dateAdded"])
df["dueDate"] = pd.to_datetime(df["dueDate"])
return df
def carica_snapshot():
if os.path.exists(SNAPSHOT_PATH):
return pd.read_csv(SNAPSHOT_PATH)
return pd.DataFrame(columns=["cveID"])
def esegui_ciclo():
catalogo = scarica_kev()
precedente = carica_snapshot()
nuove = catalogo[~catalogo["cveID"].isin(set(precedente["cveID"]))]
maschera = nuove["vendorProject"].str.contains("|".join(VENDOR_RILEVANTI), case=False, na=False)
rilevanti = nuove[maschera]
for _, riga in rilevanti.iterrows():
priorita = "CRITICA" if riga["knownRansomwareCampaignUse"] == "Known" else "ALTA"
evento = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"cve_id": riga["cveID"],
"vendor": riga["vendorProject"],
"product": riga["product"],
"due_date": str(riga["dueDate"].date()),
"priority": priorita
}
if SIEM_ENDPOINT and SIEM_TOKEN:
requests.post(SIEM_ENDPOINT, json=evento,
headers={"Authorization": f"Bearer {SIEM_TOKEN}"}, timeout=10)
print(f"[{priorita}] {riga['cveID']} - {riga['vendorProject']} {riga['product']}")
catalogo[["cveID"]].to_csv(SNAPSHOT_PATH, index=False)
print(f"Ciclo completato: {len(rilevanti)} nuove CVE rilevanti su {len(nuove)} nuove totali.")
if __name__ == "__main__":
esegui_ciclo()
Passo 9: programma l'esecuzione periodica con la libreria schedule
Il catalogo CISA KEV non si aggiorna a orari fissi, ma un controllo ogni sei ore è un buon compromesso tra reattività e carico di rete. Usiamo la libreria schedule per evitare di configurare un cron di sistema separato.
import time
import schedule
from kev_monitor import esegui_ciclo
schedule.every(6).hours.do(esegui_ciclo)
if __name__ == "__main__":
print("Monitor KEV avviato. Primo controllo immediato...")
esegui_ciclo()
while True:
schedule.run_pending()
time.sleep(60)
In produzione, avvolgi questo script in un servizio systemd (su Linux) con riavvio automatico, così sopravvive a un reboot del server senza intervento manuale.
Passo 10: aggiungi un log persistente per l'audit
Un log su file, oltre all'invio al SIEM, è utile per l'audit interno e per dimostrare in caso di ispezione ACN che il monitoraggio è realmente attivo e non solo teorico.
import logging
logging.basicConfig(
filename="kev_monitor.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logging.info(f"CVE rilevata: {riga['cveID']} - priorità {priorita} - vendor {riga['vendorProject']}")
Ruota il file di log con logrotate se prevedi di far girare lo script per mesi: un log che cresce senza limiti finisce per riempire il disco proprio nel momento peggiore.
Passo 11: collega il flusso a un playbook di incident response
Un alert che arriva nel SIEM e non produce un'azione concreta è solo rumore. Definisci un playbook minimo collegato ai tre livelli di priorità che lo script assegna.
| Priorità | Tempo di risposta target | Azione richiesta |
|---|---|---|
| CRITICA (uso ransomware noto) | Entro 4 ore lavorative | Verifica esposizione, patch immediata o mitigazione temporanea, isolamento se necessario |
| ALTA (scadenza entro 7 giorni) | Entro 24 ore | Pianificazione patch, verifica compensazioni (WAF, segmentazione) |
| MEDIA | Entro il ciclo di patch mensile | Inserimento nel backlog di patch management ordinario |
Per costruire un playbook più strutturato, la guida su come impostare un piano di incident response CSIRT copre passo passo ruoli, escalation e comunicazione interna: qui ci concentriamo solo sul collegamento tra alert automatico e prima risposta.
Passo 12: testa lo script prima di metterlo in produzione
Prima di lasciarlo girare senza supervisione, verifica tre scenari: primo avvio senza snapshot, secondo avvio senza nuove voci, e un finto errore di rete verso il SIEM (ad esempio spegnendo temporaneamente l'endpoint). Un test rapido con pytest copre già il caso più critico, quello del filtro sui vendor.
import pandas as pd
from kev_monitor import VENDOR_RILEVANTI
def test_filtro_vendor_trova_microsoft():
df = pd.DataFrame({
"vendorProject": ["Microsoft", "AziendaSconosciuta"],
"cveID": ["CVE-2026-00001", "CVE-2026-00002"]
})
maschera = df["vendorProject"].str.contains("|".join(VENDOR_RILEVANTI), case=False, na=False)
risultato = df[maschera]
assert len(risultato) == 1
assert risultato.iloc[0]["cveID"] == "CVE-2026-00001"
Esegui con pytest test_kev_monitor.py -v. Se il test passa, il filtro vendor funziona come previsto e puoi passare alla fase di programmazione periodica con maggiore fiducia.
Errori comuni da evitare
- Confrontare l'intero catalogo invece dello snapshot precedente: senza salvare uno stato, ogni esecuzione rialerta le stesse migliaia di CVE già note, saturando il SIEM.
- Hardcodare il token del SIEM nello script: usa variabili d'ambiente o un secret manager, mai testo in chiaro nel codice sorgente.
- Ignorare gli errori HTTP del feed CISA: il servizio può avere manutenzioni programmate; senza gestione delle eccezioni lo script si interrompe e smette di monitorare in silenzio.
- Filtrare per vendor con match esatto invece che parziale: "Microsoft Corporation" e "Microsoft" nel campo vendor possono differire leggermente tra un aggiornamento e l'altro del catalogo.
- Non gestire il fuso orario nei timestamp: confrontare date naive con date timezone-aware genera eccezioni difficili da diagnosticare in produzione.
- Eseguire lo script come processo interattivo invece che come servizio: un terminale chiuso per errore interrompe il monitoraggio senza alcun avviso.
- Dimenticare di validare il JSON ricevuto: se CISA cambia leggermente lo schema del feed, uno script troppo rigido va in errore invece di segnalare l'anomalia in modo controllato.
Consigli avanzati per un uso in produzione
Una volta che lo script base funziona, ci sono alcuni miglioramenti che vale la pena introdurre prima del rollout definitivo. Il primo è l'arricchimento con il punteggio CVSS ufficiale tramite l'API pubblica del NVD, così ogni evento inviato al SIEM porta sia il criterio "sfruttata attivamente" del KEV sia il punteggio di gravità tecnica, utile per la reportistica verso il management.
Il secondo miglioramento riguarda la deduplicazione multi-fonte: se la tua azienda usa anche uno scanner di vulnerabilità come quelli descritti nel confronto tra Nessus e OpenVAS, ha senso incrociare l'output del KEV con gli asset realmente rilevati come vulnerabili dallo scanner, invece di trattare ogni CVE del catalogo come rilevante a prescindere dall'inventario reale.
Terzo, valuta un secondo canale di notifica indipendente dal SIEM (una mail o un webhook Slack/Teams) per le sole priorità CRITICHE: se il SIEM stesso è irraggiungibile o sotto attacco, l'unico canale di allerta non deve essere anche l'unico punto di failure.
Infine, documenta il processo. In un contesto NIS2, avere una pagina interna che spiega come funziona il monitoraggio KEV, chi riceve gli alert e quali sono i tempi di risposta target è parte integrante della dimostrazione di conformità richiesta durante le ispezioni ACN.
Come si integra con NIS2 e gli obblighi europei
La direttiva NIS2, recepita in Italia con obblighi che coinvolgono decine di migliaia di aziende, chiede esplicitamente processi di gestione delle vulnerabilità documentati e ripetibili. Un monitoraggio automatico del catalogo CISA KEV, con log persistente e collegamento a un playbook di risposta, copre proprio questo requisito: dimostra che l'organizzazione non aspetta un audit annuale per accorgersi di una falla sfruttata attivamente, ma la intercetta nell'arco di ore.
Va inoltre ricordato che, in caso di violazione di dati personali collegata allo sfruttamento di una CVE nota, la notifica al Garante Privacy va effettuata entro 72 ore, mentre la segnalazione al CSIRT Italia passa dal portale csirt.gov.it. Avere già in mano il CVE ID, il vendor coinvolto e la data di rilevamento, tutti campi che lo script produce automaticamente, accelera in modo concreto la stesura di queste comunicazioni obbligatorie.
Il contesto italiano rende questo tipo di automazione ancora più rilevante. Il rapporto TIM sulla cybersecurity in Italia ha registrato un aumento del 14% degli attacchi ransomware nel Paese, con 166 casi rilevati, mentre il rapporto Clusit colloca l'Italia al primo posto in Europa per numero di attacchi subiti nel 2026, con 507 incidenti registrati. In un contesto del genere, sapere quali vulnerabilità sono realmente sotto sfruttamento attivo, invece di rincorrere ogni CVE pubblicata, è quello che separa un team che reagisce in tempo da uno che scopre il problema solo dopo l'incidente. Anche il catalogo stesso è in continua crescita: le aggiunte periodiche pubblicate da CISA, come quelle raccontate nella cronaca delle ultime falle aggiunte al KEV, mostrano quanto sia frequente la necessità di ricontrollare il catalogo.
Perché il KEV non basta da solo
Il catalogo CISA KEV è un ottimo filtro di priorità, ma resta comunque una lista pubblica: chi la consulta è chiunque, inclusi gli attaccanti che possono usarla come mappa delle vulnerabilità note per scegliere il prossimo bersaglio. Per questo il monitoraggio del KEV va sempre affiancato da altre pratiche difensive: patch management regolare anche per le CVE non ancora incluse nel catalogo, autenticazione a più fattori sugli accessi critici come VPN e console amministrative, e backup testati secondo la regola 3-2-1. Il KEV riduce il rumore e concentra l'attenzione, ma non sostituisce un programma di sicurezza completo basato anche sulle raccomandazioni dell'OWASP per le applicazioni esposte e sui principi di difesa in profondità.
Vale anche la pena tenere d'occhio i cataloghi paralleli pubblicati da altre agenzie europee, dato che il KEV riflette prevalentemente il contesto statunitense: incrociare i dati con gli avvisi ENISA aiuta a colmare eventuali differenze di priorità tra i due contesti geografici.
Un limite pratico da tenere presente riguarda anche la copertura del catalogo: il KEV include solo vulnerabilità per cui CISA ha raccolto prove di sfruttamento attivo, quindi una CVE appena pubblicata e già sfruttata in natura potrebbe non comparire nel catalogo per giorni o settimane, il tempo necessario a raccogliere e verificare le segnalazioni. Per questo motivo il monitoraggio KEV va trattato come un livello di priorità aggiuntivo sopra un normale processo di patch management, non come sostituto dell'aggiornamento regolare di tutti i sistemi esposti a Internet.
Risoluzione dei problemi più comuni
| Problema | Causa probabile | Soluzione |
|---|---|---|
Errore ConnectionError al download del feed | Proxy aziendale blocca il dominio cisa.gov | Aggiungi un'eccezione firewall/proxy per il dominio, verifica con curl -I |
JSONDecodeError durante il parsing | Il feed ha restituito una pagina di errore invece del JSON | Controlla lo status code della risposta prima di chiamare .json() |
| Lo script segnala di nuovo le stesse CVE ogni giorno | Lo snapshot non viene salvato correttamente o il path è diverso ad ogni esecuzione | Usa un path assoluto per SNAPSHOT_PATH, non relativo alla working directory |
| Il SIEM non riceve gli eventi | Token scaduto o endpoint errato | Verifica il token con una chiamata di test tramite curl prima di lanciare lo script |
KeyError: 'knownRansomwareCampaignUse' | CISA ha aggiornato lo schema del feed | Usa .get() con valore di default invece dell'accesso diretto alla chiave |
| Lo scheduler smette di funzionare dopo qualche giorno | Il processo Python è stato terminato dal sistema (OOM killer o riavvio) | Esegui come servizio systemd con Restart=always |
| Troppi falsi positivi nel filtro vendor | La lista vendor contiene termini troppo generici | Usa nomi vendor completi e specifici, rivedi la lista trimestralmente |
| Date confrontate in modo errato (sempre "scaduto") | Fuso orario non gestito tra dueDate e datetime.now() | Normalizza sempre a UTC con timezone.utc su entrambi i lati del confronto |
| Lo script consuma sempre più memoria nel tempo | Il file di log o lo snapshot cresce senza limiti | Configura logrotate e mantieni solo l'ultimo snapshot, non uno storico completo |
Estendere il progetto: idee per il passo successivo
Il progetto descritto in questa guida è volutamente minimo per restare comprensibile end-to-end, ma si presta a diverse estensioni naturali. Puoi containerizzarlo con Docker per semplificarne il deploy su più server, aggiungere una piccola dashboard web con Flask per visualizzare lo storico delle CVE rilevate senza dover interrogare i log, oppure collegarlo a uno strumento di ticketing (Jira, ServiceNow) così che ogni evento CRITICA apra automaticamente un ticket assegnato al team di guardia.
Un'altra estensione utile è l'integrazione con un inventario asset centralizzato: invece di un filtro statico per vendor, lo script potrebbe interrogare un CMDB per sapere in tempo reale quali prodotti sono realmente installati in azienda, eliminando quasi del tutto i falsi positivi legati a prodotti che non fanno parte del parco software reale.
Chi gestisce più sedi o più ambienti (produzione, staging, sviluppo) può inoltre valutare di parametrizzare lo script per inviare eventi a SIEM diversi a seconda dell'ambiente coinvolto, con soglie di priorità differenti: una CVE CRITICA su un sistema di produzione esposto a Internet merita un trattamento diverso dalla stessa CVE rilevata su una macchina di sviluppo isolata dalla rete pubblica. Aggiungere un campo "ambiente" al payload JSON descritto nel passo 6 è un cambiamento minimo che semplifica molto la definizione di regole di alerting differenziate lato SIEM.
Infine, se il volume di eventi cresce e il SIEM comincia a mostrare segni di rallentamento nell'ingestion, introduci un piccolo meccanismo di retry con backoff esponenziale sulle chiamate POST, invece di limitarti a un singolo tentativo. Una libreria come tenacity gestisce questo pattern con poche righe di configurazione e riduce il rischio di perdere eventi durante picchi di traffico o brevi indisponibilità del SIEM.
Quanto tempo si risparmia rispetto al controllo manuale
Vale la pena fare due conti prima di decidere se questo progetto merita le ore di sviluppo iniziali. Un analista che controlla manualmente il catalogo KEV una volta al giorno, confrontando a occhio le nuove voci con un elenco di prodotti aziendali, impiega in genere tra dieci e venti minuti per ogni controllo, a seconda di quante voci sono state aggiunte. Su base annua, con un controllo quotidiano nei giorni lavorativi, si tratta di svariate decine di ore dedicate a un compito ripetitivo e soggetto a errore umano, soprattutto nei momenti di carico di lavoro più alto quando il controllo tende a essere rimandato o fatto più in fretta.
Lo script descritto in questa guida esegue lo stesso controllo in pochi secondi, ogni sei ore, senza dipendere dalla disponibilità di una persona specifica in un dato momento. Il vantaggio non è solo di tempo: un controllo automatico non salta un giorno perché l'analista è in ferie o impegnato su un incidente diverso, e non introduce l'errore tipico del confronto manuale, cioè perdere una voce tra le tante quando il catalogo cresce di molte righe in un colpo solo.
Metriche da monitorare nel tempo
Una volta che il sistema gira in produzione da qualche settimana, ha senso raccogliere alcune metriche per capire se sta funzionando come previsto e per avere numeri concreti da presentare al management. La prima è il tempo medio di risposta per priorità: quanto tempo passa, in media, tra l'invio dell'evento CRITICA al SIEM e la chiusura del ticket di remediation collegato. Se questo numero supera sistematicamente le 4 ore target definite nel playbook, è un segnale che il processo di risposta, non lo script di monitoraggio, ha bisogno di una revisione.
La seconda metrica utile è il tasso di falsi positivi del filtro vendor: quante CVE segnalate come rilevanti si rivelano poi non applicabili all'ambiente reale, magari perché riguardano una versione di prodotto mai installata in azienda. Un tasso di falsi positivi elevato, sopra il 30-40% delle segnalazioni, indica che la lista vendor è troppo generica e va raffinata con nomi di prodotto più specifici invece che semplici nomi di vendor. La terza metrica, più semplice ma altrettanto utile, è il numero di CVE CRITICHE intercettate nell'arco dell'anno rispetto al numero totale di voci aggiunte al catalogo KEV: aiuta a capire quanto la lista di vendor rilevanti copre davvero l'esposizione reale dell'azienda rispetto al panorama complessivo delle minacce sfruttate.
Domande frequenti
Serve una licenza o una chiave API per accedere al catalogo CISA KEV?
No, il feed CSV e JSON è pubblico e non richiede autenticazione. Basta una richiesta HTTP GET standard.
Con che frequenza si aggiorna il catalogo?
Non ha una cadenza fissa: CISA aggiunge nuove voci quando identifica sfruttamento attivo, il che può significare più aggiornamenti in una settimana o nessuno per diversi giorni. Un controllo ogni 6 ore è un buon equilibrio.
Lo script funziona anche senza un SIEM aziendale?
Sì. Puoi disattivare la parte di invio API e usare solo il log su file o un webhook verso Slack/Teams come canale di notifica alternativo.
Il KEV copre anche le vulnerabilità software italiane o europee?
Il catalogo è mantenuto da un'agenzia statunitense e riflette prevalentemente prodotti globali molto diffusi. Per un quadro più europeo, integra i dati con gli avvisi ENISA e con le segnalazioni del CSIRT Italia.
Cosa succede se una CVE nel KEV non ha una patch disponibile?
In quel caso la priorità resta alta ma l'azione richiesta cambia: si passa a mitigazioni compensative come segmentazione di rete, regole WAF specifiche o disattivazione temporanea della funzionalità vulnerabile, fino al rilascio della patch.
Questo script sostituisce uno scanner di vulnerabilità come Nessus o OpenVAS?
No, sono complementari. Lo scanner verifica quali sistemi nel tuo ambiente sono davvero vulnerabili; il monitoraggio KEV ti dice quali vulnerabilità nel mondo vengono sfruttate attivamente in questo momento. Usarli insieme dà priorità più accurate.
Quanto tempo richiede la configurazione iniziale?
Seguendo tutti i passaggi di questa guida, tra 30 e 45 minuti per chi ha già familiarità con Python. Il primo test end-to-end verso un SIEM reale può richiedere qualche minuto in più per la configurazione del token.
Devo aggiornare manualmente la lista dei vendor rilevanti?
Sì, è una revisione manuale ma poco frequente: basta aggiornarla ogni volta che cambia in modo significativo l'inventario software aziendale, indicativamente ogni trimestre.
Posso usare questo approccio anche per monitorare altre fonti di vulnerabilità oltre al KEV?
Sì, la stessa architettura a passi (download, confronto con snapshot, filtro, priorità, invio) si adatta facilmente ad altre fonti che pubblicano feed strutturati, come gli avvisi ENISA o i bollettini di sicurezza dei singoli vendor. Cambia solo la funzione di parsing iniziale, il resto della logica resta identico.




