Home › Blog › Codice vulnerabile: come riconoscerlo e correggerlo

Codice vulnerabile: come riconoscerlo e correggerlo

Il codice vulnerabile è codice sorgente che contiene un difetto sfruttabile da un attaccante per violare la riservatezza, l'integrità o la disponibilità di un sistema. Si riconosce con una combinazione di revisione manuale, analisi automatica e test di sicurezza, e si corregge eliminando la causa del difetto, non solo il sintomo. In questa guida vediamo esempi concreti, strumenti e un metodo pratico per intervenire.

Cos'è il codice vulnerabile

Una vulnerabilità nasce quando il software si comporta in modo diverso da quanto previsto dagli sviluppatori, e quella differenza può essere sfruttata. Non si tratta sempre di un errore "grave" di programmazione: spesso è una svista piccola, come un controllo mancante su un input o una configurazione lasciata di default.

Per un responsabile IT conviene distinguere tre concetti:

Il codice insicuro può trovarsi nella logica applicativa scritta internamente, nelle librerie di terze parti, nei file di configurazione o nell'infrastruttura definita come codice. Per questo la sicurezza non riguarda solo il lavoro del team, ma anche tutto ciò che il progetto importa.

Esempi comuni di codice vulnerabile

Le categorie più ricorrenti sono documentate da tempo da progetti come OWASP. Ecco quelle che si incontrano più spesso nelle revisioni.

Injection (SQL, comandi, template)

Succede quando l'input dell'utente viene concatenato direttamente in una query, in un comando di sistema o in un template. Un attaccante può alterare il significato dell'istruzione e leggere o modificare dati che non dovrebbe raggiungere. Costruire una query unendo stringhe invece di usare parametri è il caso classico.

Cross-site scripting (XSS)

Se un'applicazione inserisce nella pagina contenuti forniti dall'utente senza codificarli correttamente, un attaccante può far eseguire script nel browser di altre persone. Le conseguenze vanno dal furto di sessione alla manipolazione dell'interfaccia.

Controllo degli accessi errato

Un'API che verifica l'identità dell'utente ma non i suoi permessi sul singolo oggetto consente di leggere dati altrui cambiando semplicemente un identificativo nella richiesta. È un problema di logica, difficile da trovare con strumenti automatici.

Gestione errata di segreti e credenziali

Password, token e chiavi inseriti nel repository, nei log o nel codice client restano accessibili a chiunque abbia lettura sul progetto, anche anni dopo il commit originale.

Dipendenze obsolete o compromesse

Una libreria con una vulnerabilità nota rende fragile l'intera applicazione, anche se il codice scritto dal team è corretto. Lo stesso vale per pacchetti non più mantenuti.

Deserializzazione e validazione insufficiente

Accettare dati strutturati da fonti non fidate senza verificarne forma e contenuto può portare a comportamenti imprevisti, fino all'esecuzione di codice arbitrario in alcuni linguaggi.

Come individuare il codice vulnerabile

Nessuna tecnica da sola basta. Gli approcci si completano e vanno combinati nel ciclo di sviluppo.

Un segnale utile è la presenza di input non fidato che raggiunge un punto sensibile (database, file system, shell, risposta HTML) senza passaggi di validazione o codifica. Seguire il percorso del dato dall'ingresso all'uso è spesso il modo più rapido per individuare il problema.

Come correggere il codice vulnerabile: passi pratici

Correggere significa rimuovere la causa del difetto e assicurarsi che non si ripresenti. Un metodo ordinato riduce il rischio di interventi superficiali.

  1. Riproduci il problema. Prima di modificare qualsiasi cosa, verifica che la vulnerabilità sia reale e capisci in quali condizioni si manifesta. Un caso di test che la riproduce sarà utile anche dopo.
  2. Valuta la gravità. Considera quali dati sono esposti, se serve autenticazione per sfruttarla e quanti sistemi coinvolge. Serve a stabilire le priorità.
  3. Individua la causa radice. Spesso lo stesso errore è replicato in più punti. Cercare il pattern nel resto della base di codice evita di correggere un solo caso.
  4. Applica la correzione corretta. Usa il meccanismo previsto dal framework: query parametrizzate per i database, codifica dell'output per l'HTML, controlli di autorizzazione centralizzati per gli accessi.
  5. Preferisci le soluzioni strutturali ai filtri. Una lista di caratteri vietati si aggira facilmente; una validazione basata su ciò che è consentito è più solida.
  6. Aggiorna o sostituisci le dipendenze. Se la fragilità è in una libreria, passa a una versione corretta o valuta un'alternativa mantenuta.
  7. Ruota i segreti esposti. Rimuovere una chiave dal repository non basta: va considerata compromessa e sostituita.
  8. Aggiungi un test di regressione. Il caso che riproduceva il problema deve diventare un test automatico, così la correzione resta protetta nel tempo.

Ottimizzare il codice insicuro senza introdurre nuovi difetti

Quando si rifattorizza per migliorare sicurezza e manutenibilità, conviene procedere per piccoli passi: centralizzare validazione e autorizzazione, ridurre la superficie esposta, eliminare le funzioni non utilizzate e applicare il principio del privilegio minimo. Ogni modifica deve essere coperta da test, altrimenti si rischia di spostare il problema invece di risolverlo.

Revisione del codice e test di sicurezza

La prevenzione funziona meglio quando fa parte del flusso di lavoro quotidiano. Alcune pratiche aiutano a intercettare i difetti prima del rilascio:

Gli strumenti automatici sono un ottimo primo filtro, ma non sostituiscono il giudizio umano. Una revisione attenta del codice più sensibile, come autenticazione, pagamenti e gestione dei dati personali, resta insostituibile.

Come un penetration test verifica la correzione

Dopo l'intervento serve una verifica indipendente. Un penetration test simula il comportamento di un attaccante reale contro l'applicazione o l'infrastruttura, e permette di confermare che la correzione funzioni davvero e che non esistano percorsi alternativi per sfruttare lo stesso difetto.

Un test ben condotto di solito comprende:

Il retest è la fase che chiude il ciclo: dimostra che ogni correzione è efficace e fornisce evidenza utile anche ai fini di audit e conformità. Per le applicazioni che cambiano spesso, è consigliabile ripetere la verifica a intervalli regolari e dopo ogni modifica rilevante.

Domande frequenti

Che cosa significa codice vulnerabile?

Significa codice che contiene un difetto di sicurezza sfruttabile da un attaccante. Può trovarsi nel codice scritto internamente, nelle librerie esterne o nella configurazione.

Come si trova una vulnerabilità nel codice?

Combinando analisi statica, controllo delle dipendenze, test dinamici e revisione manuale. Seguire il percorso dei dati non fidati verso i punti sensibili aiuta a individuare i casi più comuni.

Basta aggiornare le librerie per risolvere il problema?

No. L'aggiornamento risolve le vulnerabilità note delle dipendenze, ma non gli errori presenti nel codice applicativo, come i controlli di accesso incompleti o la gestione scorretta dell'input.

Gli strumenti automatici bastano?

Sono utili ma incompleti. Trovano bene i pattern noti, mentre gli errori di logica richiedono revisione manuale e test mirati.

Ogni quanto conviene fare un penetration test?

Dipende dal rischio e dalla frequenza di modifica del sistema. In generale è opportuno eseguirlo prima di rilasci importanti, dopo cambiamenti significativi e con cadenza periodica.

Come evitare che lo stesso difetto si ripresenti?

Correggendo la causa radice, centralizzando i controlli di sicurezza, aggiungendo test di regressione e formando il team sui pattern di errore ricorrenti.