Quanto sale nella password?

2 gennaio 2013 - In: Programmazione - Tag: , , , 0 Comments

Nella vita di un programmatore l’argomento “salvaguardia delle informazioni” dovrebbe occupare un posto di assoluto rilievo, tanto da influire significativamente sulla forma mentis del lavoro quotidiano. Progettare con cura l’architettura di un’applicazione senza però tenere in considerazione tecnologie e tecniche di protezione dei dati che dovranno essere gestiti, infatti, è proprio come realizzare una cassaforte con una serratura solida e inattaccabile, ma con le pareti di legno: il primo malintenzionato che passa vede un invito al furto scritto a caratteri cubitali.

La sicurezza, prima di essere un complesso di procedure e tecniche, è un atteggiamento mentale che dovrebbe essere costantemente presente nella testa di chi progetta un sistema informatico, ben prima della scrittura del codice sorgente. Un’architettura solida, in grado di prevedere le possibili vie di aggressione, produce un applicativo nativamente robusto, impenetrabile ai tipi di assalto più comuni, e spesso in grado di mantenere segrete le informazioni vitali anche dopo che sono state rubate, almeno per un tempo sufficiente ad organizzare le dovute contromisure (ad esempio: aggiornare i dati per rendere la versione rubata obsoleta, e quindi priva di utilità).

Come abbiamo visto nell’articolo precedente, il sistema che la Germania aveva adottato durante la Seconda Guerra Mondiale per la cifratura delle comunicazioni strategiche era talmente robusto da rendere inutili (per molto tempo) le intercettazioni di tali comunicazioni da parte degli Alleati. In sostanza, le informazioni non erano protette a livello fisico (i “file” viaggiavano via radio, a disposizione di chiunque volesse intercettarli), ma a livello logico: se non si era in possesso delle adeguate chiavi di decriptazione, tutto ciò che si finiva per avere in mano erano lunghe sequenze di caratteri assolutamente inintellegibili. L’informazione racchiusa in tali sequenze ha continuato quindi ad essere irraggiungibile per lungo tempo, anche se il “contenitore” e i dati racchiusi venivano sistematicamente rubati.

Certamente, in un sistema informatico moderno, questo non vuol dire che potremo sentirci liberi di mettere il nostro database server in mezzo alla strada, aperto alle incursioni del primo che passa: un’architettura di sistema accurata è il primissimo passo necessario per costruire un ambiente hardware/software di qualità. Tuttavia, un programmatore deve affidarsi a strumenti logici, più che fisici, e strutturare il proprio applicativo affinché gestisca correttamente i dati attraverso i migliori strumenti crittografici è un atteggiamento decisamente desiderabile.

Questo tipo di approccio è il più premiante in termini di efficienza nella sicurezza, perché il sistema logico che si realizza viene fondato su tre caratteristiche vantaggiose (per noi sviluppatori, almeno):
.

  • non ha bisogno di infrastrutture particolarmente fortificate (e l’esperienza insegna che non esiste fortificazione realmente impenetrabile per un aggressore dotato di sufficiente determinazione e adeguate capacità);
  • è in grado di difendere i dati anche quando l’ambiente viene violato;
  • infine (ciliegina sulla torta) offre il livello più elevato possibile di frustrazione per l’eventuale aggressore, che al termine degli sforzi necessari per attuare il furto  dei dati, finisce per ritrovarsi solo col classico, inutile pugno di mosche.

In questo articolo esploreremo le varie metodiche che i programmatori normalmente adottano per custodire le informazioni, cercando di distinguere le metodiche giuste da quelle sbagliate e, di conseguenza, come distinguere tra programmatori in gamba e programmatori della domenica.

In particolare, per proseguire il discorso avviato nel precedente articolo, concentreremo la nostra attenzione sui sistemi di conservazione delle password in un sistema informatico: concettualmente la password ci fornisce un elemento di studio assai utile e indicativo, per discutere di concetti che potranno poi essere estesi praticamente a qualsiasi elemento informativo che si desideri proteggere: gli argomenti che stiamo trattando ci riguardano così da vicino da giocare un ruolo profondamente importante nella nostra vita di tutti i giorni. E come in tutte le cose della vita, saperne un po’ di più è sempre meglio che saperne un po’ di meno.

 

Cenni storici

L’utilizzo delle password come chiavi virtuali per l’accesso a informazioni riservate è una pratica antica. Come abbiamo visto, abbiamo documentazioni storiche dell’utilizzo di cifrari fin dall’epoca pre-imperiale di Roma, e da allora la crittografia ha avuto costanti e inarrestabili progressi.

In epoca moderna, il primo utilizzo di una parola segreta utilizzata come chiave di accesso ad un sistema informatico risale agli anni ’60 del secolo scorso, quando gli ingegneri del Massachussets Institute of Technology misero a punto un sistema (noto come Compatible Time-Sharing System, o CTSS) che permettesse di ripartire tra vari utenti le allora scarse (e quindi preziose) risorse computazionali delle macchine disponibili, risorse che dovevano essere di conseguenza erogate con parsimonia e tenendo sotto stretto controllo i diversi accessi. Le varie password venivano registrate, assolutamente in chiaro, in un unico master file residente nel CTSS.

Risale allo stesso periodo, e allo stesso ambiente, anche il primo furto informatico di password della storia, messo in atto da un ricercatore che desiderava acquisire più tempo/macchina per i propri studi, e che semplicemente fece uso di una procedura consentita dal CTSS per ottenere una stampa su carta delle password di tutti gli utenti. Il ricercatore, soddisfatto del proprio exploit, in totale buona fede lo condivise addirittura con altri colleghi, anch’essi perennemente a caccia di tempo/macchina per le loro ricerche.

Nel 1965, poi, un errore nella programmazione del CTSS causò l’invio dell’intero master file a tutte le stampanti collegate, evento che richiese l’intervento degli amministratori di sistema per modificare manualmente tutte le password ormai di dominio pubblico.

Erano altri tempi, e i livelli di sicurezza raggiungevano gradi di ingenuità che oggi ci lascerebbero senza fiato; proseguendo nella lettura, però, scopriremo che anche i programmatori del 2012 sono capaci di ingenuità incredibili che, però, alla luce dei progressi compiuti ad oggi, diventano vere e proprie nefandezze informatiche, se non veri e propri atti di negligenza. A difesa dei programmatori degli anni ’60 va detto che l’hardware di allora era costosissimo, la capacità dei sistemi di memorizzazione era molto limitata, e ogni singolo bit aveva un valore immenso; di conseguenza, le risorse erano impiegate più per l’utilizzo pratico (ricerca scientifica) che non per chissà quali sistemi di protezione, senza contare il diffuso spirito di stampo universitario, più teso alla condivisione delle informazioni che alla segretezza industriale (la quale, a sua volta, non avrebbe comunque tardato a farsi conoscere, di lì a pochi anni).

 

I sistemi di conservazione più diffusi, ossia quelli sbagliati

L’iscrizione ad un forum di discussione, o ad un blog, è diventata oggi una pratica diffusissima, e non c’è piattaforma software che non abbia la sua brava procedura per la gestione dell’operazione di iscrizione dei nuovi utenti. Tipicamente avviene qualcosa del genere: il nuovo utente è invitato a compilare un modulo nel quale (tanto per fare un esempio molto semplice) deve indicare il proprio nome utente, il proprio indirizzo email, e la password che desidera utilizzare per accedere. Tecnicamente, il modulo “impacchetta” questi tre dati e li invia ad una business logic che si incarica di elaborare le informazioni, e di registrarle in un database per poterle avere sempre disponibili su richiesta.

Esistono varie soluzioni per la gestione delle password in un database; vediamo le due soluzioni sbagliate più diffuse:

  1. la registrazione in chiaro, senza il benché minimo criterio di protezione
  2. l’utilizzo del famoso algoritmo di criptazione MD5.

 

La registrazione in chiaro, ossia come aprire casa a tutti i ladri in circolazione

In moltissimi casi, le password degli utenti vengono registrate così come sono nel database; è chiaro che una scelta del genere espone gli utenti a rischi gravissimi, perché un eventuale assalto condotto contro il database server permetterebbe al ladro di turno di ottenere in un colpo solo le chiavi di accesso di tutti gli utenti registrati già bell’e pronte all’uso. Normalmente chi progetta un sistema del genere afferma che solo in questo modo è possibile soccorrere un utente che ha dimenticato la propria password e non sa più, di conseguenza, come accedere al proprio account. In realtà, una scelta del genere è un autentico orrore informatico, e se esistesse un inferno dei programmatori questo dovrebbe essere il destino senza appello di chi progetta sistemi con questi criteri.

Una procedura di soccorso più corretta, infatti, dovrebbe prevedere un sistema di criptazione delle password in modo da conservarle nel database in una forma “corazzata” non immediatamente utile ad un eventuale ladro; nel caso in cui un legittimo utente avesse dimenticato la propria password, il sistema dovrebbe obbligarlo a generarne una nuova, in sostituzione di quella perduta, dato che la password dovrebbe essere criptata in modo irreversibile, tale per cui nemmeno gli amministratori del sistema siano in grado di recuperarla. Un’architettura del genere ha molti vantaggi: impedisce ad un ladro di avere password immediatamente utilizzabili; mette gli utenti al sicuro, dato che nemmeno un eventuale amministratore in malafede può recuperare la password e sostituirsi ad un legittimo utente; contemporaneamente solleva gli amministratori fedeli dalla responsabilità di custodire password facilmente utilizzabili e/o recuperabili.

Come fare per verificare se il sistema che stiamo utilizzando (un forum, un blog, la nostra banca online, ecc.) appartiene a questa prima categoria? Molto semplice: basta provare ad utilizzare la procedura di recupero della propria password, fingendo di averla dimenticata. Se il sistema vi spedisce in email la vostra vecchia password, perfettamente leggibile, allora saprete immediatamente di essere nelle mani di programmatori della domenica: le vostre informazioni sono state registrate in un sistema sicuro come un pollaio dotato di libero accesso per le volpi. Immaginiamo ad esempio un forum privato in cui discutete di dettagli riservati della vostra vita (ad esempio la vostra salute, o i vostri orientamenti sessuali): tutte queste informazioni potrebbero cadere in mano sbagliata con una facilità disarmante. Consiglio spassionato: evitare come la peste sistemi del genere.

Se invece il sistema vi spedisce in email una nuova password generata automaticamente, raccomandandovi poi di modificarla il prima possibile con una differente scelta da voi, allora saprete che il programmatore è un tipo in gamba, con un adeguato livello di attenzione alla sicurezza del sistema e dei dati in esso conservati.

Se volete procedere con la scelta di una nuova password che sia adeguatamente robusta, sapete già come fare ;-)

 

MD5, un classico che non tramonta mai (anche se dovrebbe)…

Nei casi più evoluti, come abbiamo detto, l’applicativo gestisce la password sottoponendola ad un’operazione di criptazione prima del salvataggio nel database, con lo scopo di renderla non immediatamente disponibile ad un eventuale ladro. L’algoritmo di criptazione che si utilizza ancora diffusamente (soprattutto in Italia) è il notissimo MD5 (acronimo per Message Digest algorithm 5).

Data la sua diffusione, e la sua importanza storica, è necessario approfondire la conoscenza di questo algoritmo; ci sarà così più facile apprezzarne i meriti e stimarne le debolezze, con lo scopo di capire se e come impiegarlo in un applicativo personalizzato.

MD5 è un algoritmo crittografico di hashing (dall’inglese to hash, “pasticciare”, “scombinare”) a 128 bit, la cui funzione consiste nel mappare una stringa di lunghezza variabile e arbitraria (anche pari a zero), detta stringa di input, in una stringa di lunghezza fissa e predeterminata detta checksum MD5 o, più frequentemente, hash MD5; questa lunghezza è esattamente di 32 caratteri esadecimali, ciascuno codificato da 4 bit; da un punto di vista funzionale, dunque, MD5 può essere considerato un algoritmo a 32 bit (il funzionamento intimo di MD5 esula largamente dagli intenti di questo scritto; se siete interessati a studiare la teoria di questo algoritmo potete iniziare dalla lettura della RFC1321).

Ecco un paio di esempi pratici in cui ad una password in chiaro è associato il proprio hash MD5:

.

Password in chiaro Hash MD5
miapassword123 d3a6008c1554967ef94f7479856cb6f1
miapassword124 83073baea4ca8780df64e3686e59286b

 

Un elemento molto interessante che si può notare osservando i due esempi riportati nella tabella consiste nel fatto che variazioni anche minime nella stringa di input causano modifiche profonde nel risultante hash MD5: nel nostro caso, cambiando un solo carattere della password si ottiene un hash radicalmente differente. L’algoritmo MD5 è stato progettato proprio per offrire la massima variabilità possibile, con lo scopo di rendere estremamente improbabile che due stringhe di testo differenti, anche solo per minime variazioni, possano generare un hash identico.

Tecnicamente, un evento del genere è definito collisione.

La collisione è un evento profondanente infausto per un algoritmo crittografico, perché ne mina alla base una delle peculiarità più desiderate, ossia la capacità di produrre hash unici in grado di descrivere una ed una sola stringa di input. Concettualmente è come avere una serratura che può essere aperta da più chiavi diverse, invece che dalla vostra, e soltanto dalla vostra.

Il problema delle collisioni affligge tutti gli algoritmi di hashing esistenti, e deriva da una condizione assai banale ma dalle conseguenze molto pesanti, descritta nel cosiddetto principio dei cassetti.

Molto brevemente, tale principio afferma che se N+X oggetti (dove X≥1) devono essere inseriti in M cassetti (dove M=N), allora ci sarà almeno un cassetto contenente almeno due oggetti. Gli anglosassoni, con grande spirito di praticità, hanno chiamato questo principio legge della piccionaia: una piccionaia dotata di N celle può contenere al massimo X piccioni (dove X=N), se si desidera che in ogni cella sia presente solo un piccione.

In questa seconda formulazione appare molto chiara la banalità del principio. Tuttavia, questa banalità costituisce il fondamento di eventi molto complessi.

In particolare, conoscendo la struttura dell’hash risultante da un’operazione di codifica MD5, possiamo prevedere quanto grande sia la probabilità che almeno una collisione si verifichi, e soprattutto possiamo intuire che non può esistere un algoritmo di hashing esente da tale problema!

Come abbiamo già visto, infatti, l’algoritmo MD5 genera in output un hash di 32 caratteri esadecimali; concettualmente è come se avessimo una piccionaia con 32 celle disponibili (quindi, un numero finito di celle). Il numero di stringhe di input (ossia di possibili combinazioni di caratteri utilizzati in una password), che corrispondono ai piccioni dell’esempio, invece, è potenzialmente infinito, sicuramente molto più grande di 32. Appare così immediatamente chiaro che il numero di “piccioni” è sempre infinitamente più grande del numero (fisso) di celle della “piccionaia”. Di conseguenza, prima o poi due piccioni (due stringhe di input differenti) finiranno per occupare la stessa cella (produrranno due hash identici): si verificherà inevitabilmente, cioé, una collisione.

Tutto ciò ci fornisce una lezione importante sull’algoritmo MD5 in particolare, e sugli algoritmi di criptazione in generale: se è vero (com’è vero) che le collisioni sono inevitabili, il “terreno di battaglia” si sposta allora sulla probabilità che un tale evento possa verificarsi. Più bassa è questa probabilità, più robusto è l’algoritmo, dato che richiederà più tempo per un eventuale assalto di forza bruta.

Salvare nel database l’hash MD5 della password scelta dal titolare dell’account, anziché la password stessa, sembrerebbe dunque una buona soluzione, che ci evita di registrare un’informazione critica in chiaro, a tutto vantaggio della sicurezza. L’hash MD5, infatti, non è la password e se si prova ad utilizzarlo per effettuare il login, l’applicazione non lo riconosce come una password valida. In fase di login, infatti, l’applicazione effettua in tempo reale la codifica MD5 della password inserita da chi sta tentando di accedere, e confronta l’hash così ottenuto con quello registrato nel database; se i due hash corrispondono, allora la password inserita è identica a quella stabilita dal titolare dell’account; dunque, essa è valida e l’utente ottiene l’accesso al sistema. In caso contrario, il login fallisce e l’utente non può accedere ai contenuti riservati.

Perché invece abbiamo inserito questo esempio tra i casi di sistemi sbagliati per la protezione dei dati?

Lo spaventoso incremento della pura potenza computazionale dei processori oggi in circolazione, unitamente all’individuazione di metodiche ben affermate di assalto criptoanalitico all’algoritmo MD5, ne hanno decretato fin dagli ultimi anni ’90 la definitiva compromissione, tanto che oggi l’algoritmo non è più considerato affidabile per operazioni che richiedano un adeguato grado di resistenza.

Negli ultimi anni sono stati di fatto condotti assalti su MD5 in grado di individuare collisioni nel giro di pochi secondi su macchine dotate di CPU Pentium 4 @ 2.6 GHz. Inoltre, la grandissima diffusione di processori grafici (GPU) nel normale mercato consumer ha messo a disposizione di chiunque uno strumento molto potente per operazioni di number crunching intensivo quali, appunto, gli assalti criptoanalitici.

I processori grafici hanno avuto negli ultimi anni un autentico boom, soprattutto a seguito delle sempre più elevate richieste di calcolo dei giochi e delle applicazioni tridimensionali di ultima generazione. Con la necessità di calcolare in tempo reale milioni di poligoni al secondo (con effetti speciali quali antialiasing, effetti di illuminazione fotorealistica, superfici traslucide, ecc.), è stato necessario sviluppare processori dedicati da affiancare alle normali CPU. Le più moderne GPU in circolazione, dotate ciascuna di migliaia di core ottimizzati per il calcolo parallelo spinto, hanno prestazioni molto superiori anche alle più performanti CPU di ultima generazione (che invece sono dotate di pochi core, tipicamente da quattro a sedici), e non c’è voluto molto perché venissero impiegate in ambiti completamente diversi dalla grafica 3D: potenza pura a costi tutto sommato contenuti, cosa chiedere di più?

Una normale GPU NVIDIA GeForce 8400GS è in grado di analizzare circa 18 milioni di hash al secondo, mentre una GeForce 8800 Ultra supera i 200 milioni di hash al secondo. Hardware del genere (che fino a pochi anni fa era appannaggio esclusivamente dei racconti di fantascienza) è oggi alla portata di chiunque, e non sono rari i casi di appassionati che hanno allestito macchine con più schede video in parallelo con lo scopo di raggiungere potenze di calcolo veramente impressionanti: ad esempio con due GPU NVIDIA Serie 8 in configurazione SLI si superano i 4 miliardi di hash per secondo. E  ancora non stiamo parlando di GPU di ultimissima generazione.

In condizioni del genere (e con il continuo incremento delle prestazioni dei processori), è essenziale dotarsi di strumenti crittografici che riducano il più possibile l’eventualità di una collisione, oppure che rendano difficoltoso l’approccio computazionale, ad esempio complicandolo tanto da poterlo rallentare in maniera significativa.

Questi obbiettivi sono raggiungibili in vari modi: ad esempio è possibile aumentare la grandezza dell’hash generato dall’algoritmo crittografico, scegliendo un nuovo algoritmo crittografico che sia anche ottimizzato per la lentezza (sembra strano dirlo), al fine di sfavorire gli assalti di forza bruta dove invece la velocità è uno degli elementi fondamentali.

Inoltre, esistono delle metodiche in grado di irrobustire i dati che si desidera criptare (la password, nel nostro caso), in modo da rendere praticamente inutili approcci criptoanalitici più sofisticati come gli assalti basati su dizionario.

Queste soluzioni, che sono un misto di tecnica e di tecnologia, ci portano nella direzione giusta per iniziare a capire come strutturare un sistema adeguato, moderno ed efficace, per la conservazione sicura delle password dei nostri utenti.

 

La password, con un po’ di sale

Un sistema molto pratico, e largamente diffuso, per tagliare fuori realisticamente ogni possibile assalto basato su dizionario consiste nell’alterare la password in modo tale da sconvolgerne completamente il senso (nel caso l’utente abbia deciso di scegliere una password formata da una o più parole di senso compiuto), o comunque aumentarne la complessità oltre il livello stabilito dall’utente. Tipicamente, in sistemi del genere la logica di gestione della password, prima di procedere con la criptazione e il successivo salvataggio nel database, genera una stringa casuale di caratteri (chiamata salt, ossia “sale”), che viene poi unita alla password prima di darla in pasto all’algoritmo crittografico. In questo modo, l’hash derivante non sarà più l’hash della password scelta dall’utente, ma di una stringa di testo più grande e complessa che a sua volta contiene la password. Come è intuibile, questa operazione è chiamata salatura oppure salting della password:
.

.
La variabile $salt contiene una stringa di testo casuale; in questo semplice esempio, la stringa è scolpita nella variabile, ma tipicamente viene generata in tempo reale da un’apposita funzione che attinge casualmente da uno spazio caratteri il più esteso possibile (lettere minuscole, maiuscole, numeri, simboli, ecc.) fino a formare la stringa della lunghezza desiderata. Il risultato è un hash profondamente differente da quello generato senza la preventiva applicazione del salt alla password:
.

Salt Password Hash MD5
  vipera 3fdbea236de49fc116fc284fc93962a3 
1o,UMgDu{6 vipera da11dbe14fd3328b706d623d765fc590 

.
Inoltre, mentre nel primo caso l’hash è aggredibile da un assalto basato su dizionario (utilizzando, ad esempio, un banalissimo dizionario contenente gli hash Md5 di tutte le parole della lingua italiana), nel secondo questa possibilità viene meno, dato che 1o,UMgDu{6vipera non è una parola realisticamente prevedibile da alcun dizionario.

È questa, dunque, la tecnica in grado di proteggere adeguatamente la nostra password?

Purtroppo, la riposta è ancora no: anche con l’esclusione di un attacco basato su dizionario, la potenza di calcolo di un sistema moderno è in grado di attaccare il nostro hash MD5 derivato da salt + password facendo uso della semplice forza bruta, e finendo per ricostruire la password nel giro di pochi minuti.

L’applicazione di un salt casuale alla password è comunque un buon passo avanti nella sicurezza, dato che come minimo permette alla nostra applicazione di discriminare facilmente due password identiche, rendendole uniche. Inoltre, utilizzando con il giusto criterio il salting delle password (ossia applicando a ciascuna password un salt unico), si impedisce il ricorso ai dizionari di hash precompilati (le cosiddette rainbow tables), con l’indubbio vantaggio di contribuire a rallentare la velocità di analisi di un potenziale aggressore.

Infine, scegliendo un algoritmo crittografico migliore di MD5, il livello di sicurezza aumenta perché aumenta la complessità dei calcoli necessari per un attacco di forza bruta, e di conseguenza il tempo necessario, permettendo di recuperare il terreno perso a causa del notevole incremento di potenza pura dei moderni processori.

È davvero sorprendente scoprire che giganti come LinkedIn, Yahoo o eHarmony (tutti oggetto di noti assalti negli scorsi mesi, con diffusione degli hash crittografici delle password rubate) non fanno uso di alcuna metodologia di salting nelle procedure di conservazione delle password dei propri utenti!

 

Oltre MD5

I lettori più smaliziati, o quelli che pensano di essere più aggiornati, a questo punto staranno sicuramente pensando ad algoritmi crittografici più evoluti, come SHA1 e i suoi successori SHA256 e SHA512.

Beh, leviamoci subito il pensiero e diciamolo chiaramente: no, gli algoritmi SHA non sono adeguati sostituti crittografici di MD5.

Il problema risiede nel fatto che tutti questi algoritmi non sono stati sviluppati con lo scopo di proteggere le password degli utenti. Al contrario, questi algoritmi sono stati disegnati per produrre hash crittografici per scopi di segnatura digitale (ad esempio autenticazione o verifica checksum di file durante la trasmissione telematica): in questi contesti, dove la velocità di trasmissione è un elemento importantissimo dell’intero meccanismo, è necessario che l’algoritmo crittografico operi con il massimo della rapidità e con il minor carico computazionale possibili.

Guarda caso, rapidità e leggerezza sono, per ovvi motivi, esattamente le caratteristiche ideali per chi desidera aggredire un hash con lo scopo di sottrarre illegalmente informazioni riservate.

Nel famoso assalto condotto qualche mese fa contro LinkedIn, sono stati sottratti circa 6,5 milioni di hash SHA1 (privi di salting preliminare) relativi alle password di utenti sparsi in tutto il mondo, compresa quella di chi vi scrive; per decriptare il 90% di questi hash, ricercatori indipendenti americani hanno impiegato non più di sei giorni: un quinto di questi hash è stato decrittato in appena trenta secondi; nelle due ore successive all’assalto già un terzo degli hash era stato decrittato, il 64% entro le successive 24 ore, e un ulteriore 26% nei cinque giorni a seguire. L’operazione di decriptazione è stata condotta con una macchina equipaggiata con quattro GPU AMD Radeon HD6990, per una velocità di calcolo di oltre quindici miliardi di hash al secondo.

I programmatori di LinkedIn hanno commesso due errori gravissimi:

  1. non hanno applicato alcun salting alle password;
  2. hanno deciso di utilizzare SHA1 come algoritmo crittografico.

Se avessero invece adottato un algoritmo espressamente pensato per la protezione di password, avrebbero contrastato in maniera significativa la potenza di analisi dell’hardware utilizzato per la decriptazione, facendola precipitare a valori davvero bassi (migliaia e non miliardi di hash al secondo), a tutto vantaggio degli amministratori di sistema che avrebbero avuto un margine temporale ben più ampio per organizzare le necessarie contromisure (ad esempio, invitare tutti gli utenti a cambiare la propria password).

 

Bcrypt, una buona soluzione al nostro problema

Bcrypt è una funzione adattativa di hashing (tecnicamente definita funzione di derivazione della chiave crittografica), basata sull’algoritmo crittografico a blocco simmetrico noto come Blowfish. Bcrypt risulta notevolmente più adeguato per lo scopo di protezione delle password perché è  ottimizzato per essere lento e computazionalmente pesante; tale caratteristica è addirittura parametrizzata in un Key Factor impostabile a piacere, che permette di regolare intenzionalmente il “costo” della procedura di generazione dell’hash crittografico in termini di tempo di calcolo e reclutamento delle risorse computazionali. Incrementando il valore del Key Factor si viene dunque a creare una condizione ampiamente sfavorevole ad un possibile assalto di forza bruta, dato che, come detto, l’algoritmo sfavorisce la rapidità di calcolo a tutto vantaggio della sicurezza delle informazioni.

In più, Bcrypt richiede espressamente la presenza di un salt da applicare alla password da criptare, generandolo in automatico se non viene fornito dal sistema, incorporando così una metodica di irrobustimento che non viene più delegata alla volontà del programmatore.

Operativamente, Bcrypt è disponibile in PHP a partire dalla versione 5.3 attraverso la funzione crypt() alla quale è necessario passare come parametro il salt generato per ciascuna password che si vuole criptare. Per verificare la corretta implementazione di Blowfish sul  sistema è sufficiente accertare la definizione della costante di sistema CRYPT_BLOWFISH, che deve ritornare il valore 1:
.

 .
Il codice necessario per impiegare la funzione è decisamente semplice:
..

.
Normalmente un’installazione standard di PHP è dotata di diversi algoritmi impiegabili dalla funzione cypt(); la selezione dell’algoritmo avviene a runtime mediante la composizione del salt. Per selezionare Blowfish come algoritmo di crypt(), bisogna costruire il salt adottando la seguente sintassi:
.

Prefisso
(4 caratt.)
Key Factor
(2 caratt. da 04 a 31)
Separatore
(1 caratt.)
Stringa Random Alfanumerica
(22 caratt.)
Terminatore
(1 caratt.)
 $2y$ 07 $ R.gJb2U2N.FmZ4hPp1y2CN
$

.
Analizziamo brevemente i singoli componenti:

  • Prefisso: composto da una stringa di quattro caratteri costanti: $2y$;questa stringa rappresenta il selettore per l’algoritmo che si desidera utilizzare nella funzione crypt(), nel nostro caso Blowfish;
  • Key Factor: come detto, rappresenta il “costo” computazionale della funzione, ed è espresso da una cifra di due numeri che è il logaritmo in base 2 del numero di iterazioni che il Blowfish deve effettuare per ottenere l’hash crittografico; il range di valori va da 04 a 31, valori al di fuori di questo range causano lo stallo della funzione;
  • Separatore: composto dal carattere $, serve a delimitare la successiva stringa alfanumerica;
  • Stringa Random Alfanumerica: è una stringa di 22 caratteri prelevati casualmente da uno spazio caratteri compreso nel range A-Z a-z 0-9, sebbene siano consentiti anche i caratteri “.” e “/”; è importante adottare un criterio solido di formazione della stringa, al fine di garantirne la miglior casualità possibile;
  • Terminatore: come il separatore, è composto dal carattere $ e serve a chiudere il salt.

 

L’utilizzo di questa funzione, dunque, offre numerosi vantaggi. Operativamente, è bene verificare che la versione PHP di cui si dispone sia almeno successiva alla 5.3, anche se per questioni di bug a carico del Blowfish

 

In conclusione

Alla luce delle considerazioni fatte fino a questo punto, siamo in grado di identificare alcuni elementi sui quali costruire delle regole fondamentali per l’allestimento del nostro applicativo:
.

  • lunghezza della password: come abbiamo visto nel precedente articolo, la lunghezza della password è un elemento fondamentale per la sicurezza, più della sua complessità; è dunque necessario obbligare sempre gli utenti a creare password non più brevi di 12 caratteri; con password così lunghe, bisognerebbe superare addirittura il concetto di “password”, per arrivare a quello di “passphrase”: una frase, anche banale ma molto lunga (ad esempio: La nonna è nata nel 1930, e si è sposata all’età di 24 anni) è immensamente più facile da ricordare rispetto ad una password come I#ze={$F:*hG, e dal punto di vista criptoanalitico è pressoché impossibile da aggredire con un assalto di forza bruta che richiederebbe (anche con la potenza di calcolo dell’hardware attuale) decine di anni di analisi;
  • complessità della password: il concetto è strettamente legato al punto precedente: più una password è complessa (ovverosia: più esteso è lo spazio dei caratteri impiegato), migliore è la sua robustezza; in tal senso è necessario obbligare gli utenti a generare password contenenti uno o più caratteri diversi da quelli alfanumerici, come simboli, segni di interpunzione, ecc.
  • salatura della password: al fine di incrementare ulteriormente la complessità della password, e soprattutto di evitare possibili assalti criptoanalitici basati su dizionario, è sempre raccomandata l’applicazione di un salt casuale, costruito secondo la sintassi che ci permette di evocare l’algoritmo Blowfish;
  • unicità della password: questo è un concetto educativo, più che una funzione implementabile nel proprio applicativo; gli utenti non dovrebbero mai, mai, mai utilizzare la stessa password per accedere a sistemi diversi. Si calcola che oggi un normale utente mantiene attivi in media 25 differenti account (posta elettronica, forum di discussione, home banking, social network, ecc.), ma utilizza per questi account solo 6,5 password differenti! La pratica del riutilizzo delle password espone gli utenti a rischi giganteschi, dato che un assalto condotto contro un sistema debole (tipicamente un forum di discussione basato su software open source non particolarmente curato) può fornire con facilità al ladro una chiave da provare anche su un sistema forte (ad esempio una banca online): se l’utente ha usato la stessa password per entrambi i servizi, può tranquillamente dire addio al proprio conto in banca. Alla luce di tutto ciò, è sempre bene prevedere un avviso che spieghi con chiarezza questo rischio, al momento di chiedere all’utente di scegliere la propria password.
  • algoritmo crittografico: se siete ancora tra coloro che fanno affidamento su MD5, avete urgentemente bisogno di aggiornare le vostre conoscenze in materia di algoritmi crittografici; la scelta di un algoritmo moderno, e quindi in grado di confrontarsi adeguatamente con la moderna tecnologia a disposizione di aggressori ben motivati, è essenziale se si desidera costruire un sistema realmente robusto, e affidabile;
  • aggiornamento continuo: un sistema costruito con i migliori criteri di sicurezza disponibili oggi, non può essere considerato affidabile indefinitamente: presto o tardi (tipicamente più presto che tardi) qualcuno troverà il modo di scardinare l’algoritmo crittografico intorno al quale avete allestito il vostro applicativo; come dire, gli esami non finiscono davvero mai.

 

Bibliografia

Le tematiche affrontate in questo articolo non hanno minimamente la pretesa di esaurire l’argomento; se siete interessati ad approfondire alcuni aspetti dell’affascinante universo della crittografia, ecco alcuni suggerimenti utili:
.

  • A Large-Scale Study of Web Password Habits – (PDF)
    di Dinei Florêncio and Cormac Herle
    2007, Microsoft Research, One Microsoft Way, Redmond, VA
  • L´uovo del cuculo
    di Clifford Stoll
    Ed. Sperling & Kupfer – ISBN 9788820010171
  • The Compatible Time Sharing System (1961-1973) – (PDF)
    di David Walden, Tom Van Vleck, et al.
    2011, IEEE Computer Society
 

Questo articolo è distribuito sotto licenza Creative Commons Attribuzione - Non commerciale - Non opere derivate - 3.0 Unported (CC BY-NC-ND 3.0). Per utilizzi di questo testo al di fuori della licenza Creative Commons, è necessario contattare l'autore per richiedere il consenso esplicito scritto.




Leave a Reply

You must be logged in to post a comment.