Come noto, una vulnerabilità è un errore di programmazione, detto in gergo bug, che può essere sfruttato per far fare a un software qualcosa di non pianificato. In particolare, scatenare comportamenti o attività che inficino la sicurezza del sistema che lo esegue.
All’origine di un problema di sicurezza, quindi, nella maggior parte dei casi c’è un errore di programmazione. Può essere di varia natura ma è quasi sempre riconducibile a un programmatore che, stanco, poco concentrato o poco competente, infila nel suo codice degli errori che non vengono immeditatamente rilevati e risolti. Quegli errori, in casi particolari, aprono proprio alle vulnerabilità, sfruttabili dagli hacker con tecniche o pezzetti di codice, vale a dire i così detti exploit.
Indice degli argomenti
Sviluppo sicuro per prevenire gli attacchi
Uno dei principi cardine della sicurezza è aggiornare i software proprio per effettuare il patching delle vulnerabilità. Quindi, in buona sostanza, applicare le correzioni ai bug rilevati da chi produce il software, aziende di sicurezza, ricercatori o sviluppatori indipendenti.
D’altro canto, negli ultimi anni si sta facendo anche un grande lavoro nel diffondere principi di sviluppo sicuro, cioè pratiche di buona programmazione che portino a ridurre all’origine le vulnerabilità. Del resto, meno errori di programmazione, meno vulnerabilità. E sebbene un bug nasca spesso per errori progettuali nello sviluppo, anche la tipologia di linguaggio di programmazione fa la sua parte.
Alcuni linguaggi più di altri, infatti, concorrono a creare situazioni nelle quali lo sviluppatore è più prono a fare errori. Dichiarazione di variabili, gestione (o non gestione) della memoria, sintassi troppo verbose: sono solo alcuni esempi di fattori legati a un linguaggio che possono portare all’errore.
Non esiste il linguaggio di programmazione sicuro e universale
Questo non significa che un linguaggio di programmazione sia insicuro, ma che per sue caratteristiche strutturali porti con maggiore facilità a commettere degli errori. Comprendere con quali linguaggi di programmazione succede più spesso non serve, dunque, a sostituirli, ma più semplicemente a investire più risorse nella revisione del codice e nella formazione degli sviluppatori. E, nei casi estremi, certo, verificare se non sia disponibile un linguaggio magari più adatto in certe situazioni, ben consci che non ne esiste uno sicuro e universale.
Stabilito questo, individuare i linguaggi forieri della maggior parte delle vulnerabilità è un buon metodo per stabilire una correlazione con il loro livello di sicurezza. Certo, si deve tenere conto anche della diffusione (più un linguaggio è diffuso, maggiore è la probabilità di trovare codice con bug), ma questo è un parametro molto complesso da stimare. Un buon compromesso lo si trova concentrandosi sul mondo open source, analizzandone le principali vulnerabilità e andando poi a vedere a quali software si rivolgono e con quali linguaggi questi sono scritti.
Stando ai dati forniti da WhiteSource, sviluppatore di soluzioni dedicate al code management in ambito open source, basati su un database che include National Vulnerability Database (NVD), gli issue tracker di GitHub e quelli dei principali progetti open source, negli ultimi dieci anni quasi il 50% delle vulnerabilità (46,9%, per la precisione) è stato rilevato in software con codice sorgente in linguaggio C. Il che, a pensarci bene, non ci stupisce.
Innanzitutto, il C è il linguaggio di programmazione più antico tra quelli attualmente in uso. Questo lo ha portato a essere più diffuso degli altri e, soprattutto, a essere utilizzato in librerie e componenti software che sono utilizzati all’interno di altri software, o per produrre a loro volta nuove librerie. Una nidificazione di codice che maschera e reitera una moltitudine di vulnerabilità.
C’è poi un discorso progettuale, che porta il programmatore C a generare bug molto gravi: la gestione della memoria, nel linguaggio creato da Dennis Ritchie, è lasciata interamente nelle mani degli sviluppatori. E non è un caso che la maggior parte delle vulnerabilità riferibili al C abbia a che fare proprio con la memoria e i così detti buffer error.
Insieme al linguaggio C, i soliti sospetti
Dietro al linguaggio C, ben distaccato con una percentuale del 16,7% delle vulnerabilità riscontrate, si trova il PHP. Anche in questo caso non è una grossa sorpresa, se non forse quella di ritrovarselo davanti al linguaggio che è in terza posizione, in virtù delle vulnerabilità per le quali è citato in giudizio: in gran parte, infatti, riguardano gli attacchi di Cross-Site Scripting (XSS), seguiti dai SQL Injection e quelli relativi alle vulnerabilità CWE-264 relative a permission, privilegi e controlli degli accessi.
A poca distanza da PHP, come prevedibile, c’è Java, a cui si deve un 11.4% delle vulnerabilità riscontrate negli ultimi dieci anni. E non stupisce che le vulnerabilità, in questo caso, siano di classe CWE-200 (“Exposure of Sensitive Information to an Unauthorized Actor”), CWE-20 (“Input validation”) e, di nuovo, CWE-79 (XSS).
Passare a un altro linguaggio o no?
Da quanto visto, significa dunque che il linguaggio C deve essere evitato? Volendo adottare un approccio draconiano la risposta sarebbe affermativa, tanto che esistono linguaggi capaci di fare le stesse cose, con quasi le stesse prestazioni (dove il C, oggi, a parità di qualità del codice, è insuperabile) e con una gestione della memoria e del compilatore moderna, automatizzata e guidata.
E in questo senso c’è chi guarda, soprattutto, allo splendido Rust, nato per risolvere i noti problemi di C e C++. In realtà, tuttavia, non esiste un buon motivo per sopprimere l’utilizzo del C, specie negli ambiti dove ancora oggi regna incontrastato (embedded software e network programming).
Si tratta, piuttosto, di analizzare proprio i preziosi dati relativi alle vulnerabilità tipiche del linguaggio e mettere in atto delle best practice di sviluppo sicuro per evitarle.
Un approccio più sicuro alla programmazione
Per esempio, non viene nuovo il dato che ci dice che oltre il 30% delle vulnerabilità imputabili a errori nel codice C siano di classe CWE-119 (“buffer errors”), e siano seguite solo a grande distanza da quelle di classe CWE-20 (“input validation”), che ammontano al 15%. Evitare i buffer error, quando si sviluppa in linguaggio C, dunque, significa sistemare buona parte dei rischi di vulnerabilità e rendere giustizia a questo leggendario linguaggio di programmazione.
Un approccio allo sviluppo sicuro, a cui dedicheremo appositi articoli, passa sicuramente per attente review e debug del codice, ma occorre ora più che mai giocare di anticipo affrontando i problemi sul nascere.
Un buon punto di partenza, in questo senso, è offerto da un libro come “Effective C”, pubblicato da No Starch Press e scritto dal bravissimo Robert Seacord, specializzato proprio nello sviluppo di codice sicuro in C e C++. Tanti ottimi suggerimenti e tecniche per scrivere codice sicuro col meno sicuro del linguaggi di programmazione.
Perché la sicurezza, in fondo, parte da cose così.