Funzioni di hash: cos’è, a cosa serve e perché è così importante conoscere l’hashing

Le funzioni di hash sono uno degli aspetti più trascurati dai neofiti quando iniziano ad approfondire lo studio delle criptovalute e questo è senza dubbio strano se consideriamo che le funzioni di hash sono il cuore pulsante della crittografia. Normalmente quando ci si accosta allo studio di una criptovaluta come per esempio bitcoin (ma vale anche per qualunque altcoin) ci si concentra sui casi d’uso e il protocollo di consenso, ma tutta la crittografia si poggia sulle funzioni di hash e questo aspetto non andrebbe mai trascurato. E’ certamente vero che raramente un progetto nuovo si affida a una nuova funzione di hash, quasi sempre si tendono ad usare funzioni la cui sicurezza è già stata comprovata, ma chi ci segue abitualmente sa che questo non è sempre vero; in uno dei nostri ultimi post dedicato a IOTA, infatti, abbiamo avuto modo di dimostrare che questo progetto fa uso di una funzione di hash sulla quale sono stati trovati diversi bug e per il quale saranno necessari anni prima di poterne attestare con certezza la sicurezza. Ma le funzioni di hash, e questo è un altro aspetto della questione che spesso sfugge, trovano numerose altre applicazioni anche fuori dalla crittografia; in questo post, quindi, proveremo a spiegare non solo cosa sono queste funzioni e a cosa servono, ma forniremo anche dei concreti casi d’uso tesi a dimostrare che la conoscenza di questi strumenti può far comodo a qualunque utente anche fuori dall’ambiente di sviluppo delle cripto.

Che cos’è una funzione di hash

Una funzione di hash, molto semplicemente, è una funzione matematica che permette di ridurre una qualunque stringa di testo (indipendentemente dalla sua lunghezza) in una nuova stringa avente precise caratteristiche tra cui un numero di caratteri predefinito. A partire da un input X, in altre parole, sarà possibile generare un input Y che avrà delle caratteristiche ben definite; questo meccanismo viene oggi comunemente utilizzato per proteggere i data base sui quali si conservano le password che permettono agli utenti l’autentificazione (in pratica il login) sui maggiori siti web. Anche google, ad esempio, usa le funzioni di hash; immaginiamo di attivare una nuova mail su google, quello che succede è che noi definiamo una password (ad esempio “carrozzella”) ma poi fisicamente la nostra password deve essere conservata su una base dati di modo che quando andremo nuovamente ad autenticarci sul sito questo potrà verificare che la password inserita durante il login coincida perfettamente con quella conservata sul data base (che è poi anche quella che noi abbiamo stabilito durante la fase di registrazione). Fisicamente sul data base di google agganciato al nostro username non ci finisce la password “carrozzella” in chiaro ma una stringa alfanumerica che il risultato di quanto prodotto dalla funzione di hash, in pratica l’impronta digitale della nostra password. Questo viene fatto perché anche qualora il data base venisse violato l’hacker si troverebbe nella condizione di dover decrittare la password sottoposta a funzione di hash se desidera riuscire effettivamente ad accedere al nostro account.

Caratteristiche delle funzioni di hash

Esistono diversi tipi di funzioni di hashing, dalle più antiche (come l’MD5) alle più moderne e comunemente considerate inattaccabili (come nel caso della SHA-256 che è la stessa usata da bitcoin e dall’NSA americana); a prescindere dal tipo di funzione di hashing tutte hanno delle caratteristiche di base comuni che andremo qui di seguito a riepilogare brevemente:

Costanza: a parità di input la stessa funzione di hash restituirà sempre la stessa stringa alfanumerica; ad ogni input, in altre parole, corrisponde sempre e inevitabilmente lo stesso output

Irreversibilità: mentre è sempre possibile riprodurre l’output conoscendo l’input originario col quale l’output è stato generato non è però possibile fare il percorso inverso, non si può quindi a partire da una stringa alfanumerica risalire al contenuto iniziale dell’input che l’ha generata

Determinismo: indipendentemente da quanto sia lungo l’input la funzione di hash restituirà sempre una stringa alfanumerica di un numero determinato di caratteri; se prendessi la divina commedia, ad esempio, e la sottoponessi a una funzione di hash il risultato sarebbe sempre una stringa di 32, 64 (etc) caratteri (la lunghezza dell’output dipende dalla funzione di hashing usata)

Effetto valanga: non importa quanto l’input sia lungo e complesso, è sufficiente una variazione infinitesimale dell’input per generare un output completamente differente; se sottoponessi la divina commedia a una funzione di hash e poi successivamente rifacessi la medesima operazione rimuovendo un solo singolo spazio dal testo originale l’output che verrebbe generato in questo secondo caso sarebbe radicalmente differente da quello generato nel primo

Descrivere tutto questo a parole è abbastanza complicato, quindi per chiarirti le idee ti consiglio di visitare questo sito http://onlinemd5.com/ e divertirti un po’ giocando con le funzioni di hash; io qui di seguito farò degli esempi, puoi provare a replicarli per capire meglio di cosa parliamo o puoi inventartene di nuovi. Proviamo a sottoporre a una funzione di hash SHA-256 un verso di una canzone di Vasco Rossi e precisamente “respiri piano per non far rumore”, bene, l’output che ci viene restituito è questo:

89ED6E63E7D77EE386C744A2CF6C9695D567EF87A88120EECEAD24B86AA26284

Ora prendiamo la stessa frase ma mettiamoci una bella maiuscola, l’output di “Respiri piano per non far rumore” sarà:

AD6783BCC47BCB30D57E627833ABB8E1F52FB8179DC8DD384295E962689035A8

Lo vedete ad occhio nudo che i due output sono radicalmente differenti, è proprio questo l’effetto valanga di cui parlavamo. Adesso invece prendiamo tutto il testo della stessa canzone e sottoponiamolo alla nostra funzione di hash SHA-256, questo sarà l’output:

0B8C47A26D67EFCD0EA065602C7100068715F45086DE89782C9F96C9CFCFAC87

Anche se l’input si compone di un numero di caratteri molto maggiore rispetto a quello di cui si componeva il singolo verso “respiri piano per non far rumore” l’output finale contiene sempre lo stesso numero di caratteri (caratteristica di “costanza”).

Vulnerabilità delle funzioni di hash

Una funzione di hash come la SHA-256 non ha vulnerabilità vere e proprie, tuttavia è comunque possibile risalire all’input iniziale che ha generato l’output a patto di conoscere il tipo di funzione di hash utilizzata. Esistono infatti delle tabelle che codificano gli input possibili restituendo per ognuno il relativo output; tali tabelle prendono il nome di “arcobaleno” ma sono strumenti molto rudimentali e che possono essere usati solo in presenza di password molto brevi. Abbiamo detto che siti come ad esempio google non custodiscono sulla propria base dati le password in chiaro ma le sottopongono a funzione di hash, se io però uso una password banale e breve, come ad esempio “1234” sarà facile che in circolazione ci sia una tabella che ha codificato tutti gli output possibili per input fino a 4 caratteri. Proprio per questo è importante utilizzare password comunque lunghe e complesse, perché non si possono generare “tabelle arcobaleno” per numeri illimitati di caratteri, una buona password che contenga almeno 18 caratteri e sottoposta a funzione di hash prima di essere conservata su un database rimane sostanzialmente blindata anche qualora il data base che la custodisce venisse bucato. In realtà gli sviluppatori hanno ideato un sistema molto semplice per aggirare questo problema che prende il nome di “sale e pepe”, in pratica le password definite dagli utenti vengono “condite” con una piccola aggiunta prima di essere sottoposte alla funzione di hash, di conseguenza quando io definisco una password del tipo “1234” il sistema la trasforma in qualcosa del tipo “blablebliolb1234” rendendola più lunga prima di sottoporla al codice di hash ed ogni volta che mi logo su quel determinato sito inserendo la mia password “1234” il sistema automaticamente ci aggiunge il “sale” (cioè la porzione di stringa “blablebliolb”) prima di raffrontarla con la password custodita sul database.

Usi concreti delle funzioni di hash nella vita di tutti i giorni degli utenti comuni

Anche se non sei uno sviluppatore puoi comunque trarre vantaggio dalla conoscenza e dall’uso delle funzioni di hash; l’uso più comune che ne puoi fare è per generare password forti per le credenziali di accesso ai siti e al contempo facili da ricordare. In questo modo potrai anche tenerne traccia per iscritto (caso mai dovessi avere un trauma cranico e perdere la memoria), restando perfettamente sicuro che anche se ti rubassero il foglio di carta su cui conservi le tue password nessuno potrebbe riuscire ad accedere ai tuoi account. Supponiamo il caso di un utente medio che abbia una mail su google, un account su twitter, uno su facebook e uno su instagram; su un pezzo di carta prendiamo nota delle nostre password e quindi scriviamo qualcosa del tipo:

password twitter: matrix film del 1999 dei fratelli andy e larry wachowski

password facebook: alba chiara di vasco rossi canzone del 1979

password instagram: la primavera del botticelli dipinta nel 1477

password gmail: le città invisibili di italo calvino romanzo del 1972

Cosa abbiamo qui? Abbiamo quattro cose facili da ricordare, perché sono (è un esempio) il mio film, la mia canzone, il mio quadro e il mio libro preferito; per evitare di fare errori nell’inserimento dell’input scrivo tutto minuscolo e senza punteggiatura. Quello che farò sarà sottoporre ognuno di questi input a una funzione di hash (ad esempio usiamo la MD5 che ha un output più corto) per ricavare le mie password. Sul mio pezzetto di carta conservo le password in chiaro ma poi per fare il login uso la stringa alfanumerica ricavata dalla funzione di hash, quindi avrò per:

“matrix film del 1999 dei fratelli andy e larry wachowski” la mia password MD5

2BAC5F2E39D59D3F0052712566117D7E

“alba chiara di vasco rossi canzone del 1979” la mia password MD5

1DF966565CC2237D440645A114B0ADE5

“la primavera del botticelli dipinta nel 1477” la mia password MD5

EE3CFF446F1B728D8CE683C65416ED78

“le città invisibili di italo calvino romanzo del 1972” la mia password MD5

65C3005EFEDF9257F1D03BA9CC0E755B

E’ impossibile dimenticare l’input usato per generare la nostra password forte perché sono cose che ci riguardano intimamente (il mio film, la mia canzone, il mio romanzo preferiti, etc); e per effettuare l’accesso mi basta avere sempre a portata di mano la funzione di hash che ho usato per ricavare l’output. In questo modo genero password forti e al contempo impossibili da dimenticare.

Un caso d’uso concreto delle funzioni di hash oltre la crittografia

Oltre che per la sicurezza le funzioni di hash possono essere usate anche per generare codici univoci avendo la certezza matematica di non generare mai due codici uguali; faccio un esempio reale che mi riguarda direttamente. Tra le mie tante collaborazioni ce n’è una con un’azienda attiva online per la quale mi occupo di gestire la rete di partner che aderiscono al nostro modello di business. Nello strutturare il metodo di lavoro sono nate due necessità: generare codici contratto univoci e generare codici sconto univoci. Certo, avrei potuto procedere attribuendo un numero progressivo ad ogni nuovo codice contratto e ad ogni nuovo codice sconto, ma il rischio era che più persone operando contemporaneamente sul sistema potessero finire col generare due codici contratto uguali. Se tre dipendenti del backoffice avessero compilato contemporaneamente un nuovo contratto c’era il rischio di generare due codici contratto uguali se solo uno dei tre non si fosse accorto di dover scalare opportunamente il progressivo. Come ho gestito questo problema? Con una funzione di hash; semplicemente vengono generati i codici utenti usando le prime lettere del nome e del cognome, la data di nascita e la targa del comune di residenza, in questo modo il signor Mario Rossi nato il 27/05/1965 e residente a Torino avrà un codice utente del tipo TO2705MARROS1965 che mi offre garanzie di univocità perché per generarsi due codici utenti uguali dovremmo avere due Mario Rossi entrambi nati lo stesso giorno e residenti nel comune di Torino (cosa che è abbastanza rara); a questo punto per generare il codice contratto uso una formula tipo “codice utente + pacchetto sottoscritto + data di sottoscrizione” e la sottopongo a una funzione di hash. In questo modo sono certo che il codice contratto sarà univoco perché per potersi generare due codici contratto uguali dovrei avere due persone omonime, nate nello stesso giorno, residenti nella stessa città, che sottoscrivono lo stesso pacchetto nello stesso giorno (sostanzialmente impossibile). Per i codici sconto faccio la stessa identica cosa usando una formula del tipo “codice contratto + data in cui lo sconto viene erogato” e la sottopongo a funzione di hash; anche qui sono sicuro di non poter mai generare due codici sconto uguali. Le funzioni di hash, quindi, possono essere usate da chiunque e con diverse finalità, non solo nell’ambito delle criptovalute, ma anche dalle persone comuni per generare password complesse ma al contempo facili da ricordare e in ambiti lavorativi qualora ci sia la necessità di generare dei codici alfanumerici univoci con la certezza matematica che non possano essere duplicati.

Conclusioni

Per chi opera in ambito informatico è impensabile non avere una conoscenza anche solo superficiale delle funzioni di hash, ma al giorno d’oggi, con la sicurezza informatica che diventa sempre più importante per tutti, anche le persone comuni possono trarre vantaggio da questi strumenti. Ovviamente così come esistono diverse esigenze esistono diverse funzioni di hash che possiamo impiegare in base ai nostri scopi; se abbiamo bisogno di aumentare la sicurezza allora la SHA-256 è sicuramente la funzione migliore in assoluto, ma per generare codici alfanumerici univoci basta e avanza anche una MD5. Con un po’ di fantasia si scopre facilmente che le applicazioni di questi strumenti sono tantissime, un aspetto che non abbiamo considerato ad esempio sono le verifiche di integrità dei documenti scaricati online; ma i campi in cui una funzione di hash correttamente impiegata può semplificarci di molto la vita sono tanti e solo chi conosce questi strumenti può capire come usarli per soddisfare le proprie esigenze. Ancora una volta, quindi, emerge come la crittografia sia capace di impattare in ogni ambito dello scibile umano arrivando a rivoluzionare i processi attraverso cui gestiamo i modelli complessi e semplificando, in certi casi anche molto, la nostra operatività garantendoci al contempo i migliori standard di sicurezza possibili.