Negli ultimi tempi si pone finalmente in modo più metodico il problema della programmazione sicura, ovvero lo scrivere programmi che diano delle garanzie sulla qualità del codice dal punto di vista della sicurezza, nell’ottica dell’assurance (“certezza” della corretta implementazione) anziché delle sole funzionalità. Per la verità, questo interesse è più spesso a parole che nei fatti, dato che la programmazione sicura, tanto per cambiare, costa.
La differenza fra funzionalità e assurance è ben descritta nei Common Criteria, ora ISO/IEC 15408, che sono l’evoluzione degli standard TCSEC, informalmente noti come Orange Book e Rainbow Series, nonché dell’ITSEC, tuttora citato in alcune normative. Il principio è: una cosa è fornire una funzionalità, ad esempio un meccanismo di autenticazione, di controllo accessi o di cifratura del traffico, un’altra è garantire che il meccanismo sia implementato in modo da non essere facilmente manomesso o aggirato. L’assurance si preoccupa del secondo aspetto, e la programmazione sicura ne dovrebbe essere una componente.
Al momento quando si parla di “programmazione sicura” sento parlare più che altro di “evitare gli errori tipici” e di audit del codice. Tuttavia, la programmazione sicura non è solo questo. Di nuovo ci vengono in aiuto i Common Criteria: solo i livelli più bassi di assurance si accontentano di test e verifiche: i livelli più alti prevedono un disegno (più o meno formale). Quello che voglio sottolineare non è tanto il disegno formale, quanto l’applicazione di principi di sicurezza al disegno del software e non solo alla scrittura del codice. Anche il codice scritto meglio non sarà mai senza difetti; per quanto si siano cercati di evitare gli errori, e per quante verifiche siano state fatte al codice, c’è sempre quindi il momento in cui concetti come segregazione, privilegio minimo ecc. entrano in gioco. Apparentemente, questi concetti vengono affrontati quando si parla di programmazione sicura. Generalemente però sono affrontati nell’ottica della configurazione dell’applicazione nel sistema (vedi chroot, capabilities ecc) e non nel disegno dell’applicazione. Per chiarire quello che intendo l’esempio migliore è postfix. Postfix è stato scritto da Wietse Venema durante un periodo di attività in un centro di ricerca di IBM. Postfix (inizialmente VMailer) è stato scritto con la precisa intenzione di creare un’alternativa sicura e robusta a sendmail, che allora era l’MTA più diffuso e che era l’incubo di sicurezza dei sistemisti. L’architettura di postfix mostra immediatamente che disegnare un’applicazione utilizzando concetti di segregazione e privilegio minimo è ben diverso dal configurarla in un ambiente chroot o dall’usare capabilities: ogni parte dell’applicazione ha accesso alle risorse di cui quella parte, e non l’intera applicazione, ha bisogno. Questa logica è molto diversa da quella dell’applicazione monolitica, che tuttora prevale, anche perché permette di affrontare il grosso problema dell’insieme di diritti variabile nel tempo necessario per operare sui dati: ogni stadio è un componente separato che opera con il proprio set di diritti, e quindi il singolo componente non ha quasi mai bisogno di acquisire o perdere diritti. Poi, su ognuno di questi sono applicati i corretti principi di configurazione sistemistica (es. chroot). Il grosso vantaggio è che un difetto in uno dei componenti ha un impatto limitato sul sistema nel suo complesso: primo, perché l’insieme limitato di diritti e funzionalità può rendere il difetto difficile da utilizzare come vulnerabilità di sicurezza; secondo, perché anche quando lo è, il danno è confinato. Nel caso di postfix ad esempio, la compromissione del componente che riceve i messaggi dalla rete permette di accedere ai nuovi messaggi in ingresso, ma non permette di accedere a quelli in coda, a quelli nelle caselle di posta, e in generale offre pochi appigli per compromettere il sistema nel suo complesso. Questa logica di contenimento è quella su cui vorrei mettere l’accento. I sistemi continueranno a diventare sempre più complessi, la mole di dati continuerà a crescere, l’accesso ai dati sarà sempre più difficile da controllare. Sia i meccanismi di sicurezza “tradizionali” che il tentativo di limitare gli errori nella scrittura del codice riusciranno solo in parte nel ridurre il rischio, lavorando sulle vulnerabilità e sulla componente probabilistica. Ma il rischio ha un’ulteriore componente che è l’impatto, e il confinamento lavora su quello. Purtroppo, chi si occupa di sicurezza ha la tendenza a considerare la compromissione e l’impatto come effetti “tutto o niente”, e questa logica è quella che secondo me bisogna superare.
In effetti, quando è nato postfix, c’era già un altro MTA che usava la stessa logica di suddivisione in più processi, ed era qmail, anch’esso scritto per contrastare l’infinito numero di bug di sendmail, e anch’esso con un considerevole successo in termini di sicurezza. Vale la pena di leggere questa pagina perché descrive molti problemi di sicurezza riconducibili al progetto e non alla scrittura del codice. Purtroppo qmail ha sofferto della personalità del suo sviluppatore, e non ha mai avuto la completezza, la flessibilità e il successo di postfix. Qui si può avere un esempio sia della personalità di Bernstein, sia della sua competenza, sia di altri problemi di sicurezza legati alla progettazione e non alla scrittura di codice. In effetti, l’unica vulnerabilità di qmail riportata nel database di Securityfoocus è quella discussa con Venema, almeno fino a quando non sono arrivate le architetture a 64 bit che quando è uscito qmail non erano probabilmente nei pensieri di Bernstein. Di nuovo quindi, sicurezza dall’inizio: buona progettazione prima della scrittura del codice.
Cercare i riferimenti agli inizi di Postfix mi ha fatto ritrovare alcuni altri riferimenti interessanti. Il primo è il perché il sito di Venema si chiama porcupine (porcospino). È sempre attuale:”Europe is a collection of countries each with their own regulations. Making progress in Europe reminds me of porcupines making love. Auch! Sorry! Look out!”. Ultimamente sto lavorando a un progetto europeo che mi ricorda proprio questa situazione 😉
Il secondo riferimento è a Satan. Satan, scritto da Wietse Venema e Dan Farmer, è stato pubblicato nell’aprile del 1995 ed è l’antesignano di Nessus e dei vari security scanner che adesso tutti usano. Allora non c’erano tool pubblci di questo tipo, anche se ovviamente qualcuno si era scritto i propri script. Quando Satan stava per uscire, c’è stato un clamore impressionante. Riporto quanto ho scritto a suo tempo su Satan:” Le reazioni alla pubblicazione di Satan furono spesso scomposte, rasentando il ridicolo. C’era chi prevedeva il blocco di Internet per la data del rilascio, chi si faceva suggestionare dal nome e le discussioni cominciarono piú di un mese prima del rilascio. Non successe niente. Di fatto, al di là del rumore sulla stampa, Satan fu forse l’unico vero flop fra i tool di Wietse. Le vulnerabilità che provava erano poche, principalmente relative alle r-utility, a NFS e a portmapper, ma era quasi obsoleto già quando fu rilasciato. Di fatto nessuno aggiornò o aggiunse nuovi plug-in e quindi divenne presto inutile. Nonostante questo, per parecchio tempo siti e persino prodotti firewall hanno vantato come “patente di invulnerabilità” il fatto di essere stati testati con Satan.”. Dan Farmer venne licenziato per aver voluto pubblicare Satan: aziende che porducevano questo tipo di strumenti negli anni successivi sono state comprate a suon di milioni di dollari.