AVVISO IMPORTANTE: Dato l'elevato numero di richieste, Eternal Curiosity non può accettare nuovi clienti, sia che si tratti di consulenze scritte che di ottimizzazione SEO diretta. Valutiamo solo casi eccezionali e ad alto budget. Per informazioni, vi invitiamo a scriverci a [email protected]

Questo articolo apparteneva al vecchio blog (blog.danilopetrozzi.it), per cui le informazioni potrebbero non essere più aggiornate ed attendibili.

Funzioni hash: a cosa servono e perché dovresti conoscerle

(questo post è dedicato sopratutto a chi non sa minimamente cosa sia un hash, ma è un ottimo ripasso anche per chi è già hash-savvy 🙂 )

Per prima cosa, partiamo con la definizione generica: le funzioni di hash (hash functions) sono, appunto, delle funzioni che a partire da un qualsiasi stringa in input A, producono una stringa B (impronta) che ha una lunghezza costante, a prescindere dalle dimensioni di A.

E ora, qualche riga motivazionale: a che mi servono le funzioni di hash? Posso vivere senza?

Che tu sia un developer, un SEO o un semplice appassionato di informatica, ti sarà sicuramente capitato nella vita di incontrare termini come MD5, digest, checksum, SHA, salt, pepper, e cose simili. Capendo il funzionamento degli hash, potrai decifrare tutti questi termini (e molti altri), dato che l’origine è comune.

Se per esempio sei un SEO/Developer/Smanettone, ti sarai sicuramente accorto che nel file wp-config.php di ogni installazione WordPress ci sono righe simili a queste:

Quando installi WordPress, queste stringhe vengono generate casualmente e inserite nel wp-config.php. Ti sei mai chiesto a che servono?

Altra “curiosità” per motivarti: ti sei accorto del fatto che siti come Google, Facebook, Twitter, Amazon, ecc, non ti permettono di recuperare la tua password ma solo di resettarla? Questo succede perché usano gli hash, al posto delle password, e quindi, anche volendo, non possono inviarti la tua password in chiaro, perché nemmeno loro la sanno.

Cos’è un hash: visione “high level”

Prima di parlare con i termini tecnici, proverò a spiegarti il concetto di hash con una visione “dall’alto”, con termini semplici.

Una funzione di hash è un algoritmo nel quale tu puoi inserire un numero qualsiasi di caratteri (uno, dieci, ventimila, quello che vuoi), e lui “sputerà fuori” un codice univoco di lunghezza fissa. In pratica, una funzione di hash rileva “l’impronta digitale” di un testo, ma da questa impronta è impossibile risalire al testo iniziale.

Aggiungiamo qualche termine tecnico

Se, ad esempio, stiamo parlando della funzione di hash MD5, in questo caso qualsiasi sia la lunghezza dell’input, l’output è sempre di 128bit, ossia 32 caratteri (dato che è in esadecimale).

Un’altra caratteristica peculiare degli hash è la costanza: se l’algoritmo scelto è lo stesso e il testo in input non cambia, anche l’output è fisso. Questo significa che se io ottengo l’hash MD5 della parola “ciao”, se domani provo a riottenere lo stesso hash, sarà identico al precedente.

Caratteristiche peculiari degli hash

Irreversibilità

Come ho anticipato prima, le funzioni di hash sono irreversibili. Questa caratteristica è molto importante, ed è anche quella che maggiormente distingue l’hashing dall’encrypting:

Hashing (ottenere un hash) è una operazione IRREVERSIBILE

Encrypting (criptare un testo) è una operazione REVERSIBILE (dato che c’è la presenza di una chiave o password)

L’hashing è irreversibile dato che conoscendo l’hash, è matematicamente IMPOSSIBILE dedurne il testo originale.

Determinismo

L’input A produce e produrrà sempre lo stesso hash B. Le funzioni di hash sono deterministiche proprio perché l’output di un input fisso è sempre uguale.

Lunghezza fissa

L’output prodotto dalle funzioni di hash ha una lunghezza fissa. Nell’esempio dell’MD5, come abbiamo già detto, gli hash in output sono sempre di 32 caratteri.

Effetto valanga

L’avalanche effect, è una proprietà per cui una piccola variazione nell’input A, produce una notevole variazione negli hash.

Alcuni hash di esempio

Di seguito, gli hash della stringa “Danilo Petrozzi ottenuti con vari algoritmi famosi:

MD5: 2795ba32c0a01fba1bd78d0ca3b41255

RipeMD128: e14407339a8b38d7a46372898694d17b

SHA-1: 7a40f85a28207a0f3c1c8b4ab6d92005e593ee6c

SHA-256: adf728d034cbe8e0c08a9fea0720782aa890e49f331fea5c483fd4fe8a03835c

Come puoi vedere, questi 4 algoritmi hanno prodotto l’impronta digitale della stringa “Danilo Petrozzi”. Dato che gli hash sono deterministici, se tu provassi a ottenere gli hash del mio nome, otterresti gli stessi risultati.

Per dimostrare le altre proprietà degli hash, come l’effetto valanga e la lunghezza fissa degli output, proviamo a fare un test. Se io cambiassi “Danilo Petrozzi” in “Danilo Petrozzo“? Che accadrebbe?

L’input è cambito di pochissimo (solo una lettera), ma vediamo come si comportano i quattro hash. In rosso ci sono gli hash della nuova stringa (Danilo Petrozzo):

MD5: 2795ba32c0a01fba1bd78d0ca3b41255

MD5: dffcf0d6acf145253e9513c866a63305

RipeMD128: e14407339a8b38d7a46372898694d17b

RipeMD128: 2dd0ee7cb79d33e9a8e663efb6e2a5b0

SHA-1: 7a40f85a28207a0f3c1c8b4ab6d92005e593ee6c

SHA-1: ab8a07304a5433ac58a233eed7cfb60e1ee39713

SHA-256: adf728d034cbe8e0c08a9fea0720782aa890e49f331fea5c483fd4fe8a03835c

SHA-256: efb76d4948afab5ee81c771a0b2b24cde8ede7b7bf55e41663303f5e8d70b23f

Come puoi vedere, gli hash mantengono una lunghezza fissa e godono dell’effetto valanga, per cui la variazione di una “i” in “o” nella stringa, ha causato una enorme variazione negli hash. Questo significa che anche se all’occhio umano Petrozzi e Petrozzo sono molto simili, osservando gli hash è impossibile trovare una correlazione tra le due.

L’ultima caratteristica che mi è difficile dimostrarvi (dato che bisognerebbe sviluppare tutto con funzioni matematiche) è il fatto che sono irreversibili. Credetemi sulla parola se vi dico che un hash è generato in modo tale da essere irreversibile.

Nel generare l’output, infatti, c’è un processo di data-loss volontario (perdita dei dati) che impedisce la reversibilità dell’operazione. Provate a pensare a questa cosa: se io prendessi tutto il testo della Bibbia e generassi il suo hash MD5, otterrei qualcosa simile a 6169e19d0f36b9e2dc5b9e253ed099db . Secondo voi sarebbe possibile ottenere tutti i centinaia di migliaia di caratteri della Bibbia grazie alla stringa 6169e19d0f36b9e2dc5b9e253ed099db?

Ancora non credete alla irreversibilità degli hash? Beh allora sai che ti dico? Questo è l’hash SHA-1 della mia password Gmail: 3db9bba24cec76ac2f6b69899d553d0750213828   Buona fortuna a decrittarla!

Perché gli hash sono utili

Dato che godono di numerose proprietà interessanti, gli hash vengono utilizzati per scopi differenti. Il più importante, sicuramente, è quello legato alla sicurezza e all’autenticazione.

In tutti i CMS moderni e in tutti i siti “decenti”, le password degli utenti vengono salvate nel database sotto forma di hash e non in chiaro. WordPress, Joomla, Drupal, e così via: tutti questi CMS usano questo sistema. La stragrande maggioranza dei siti web nel mondo, usa questo sistema. Purtroppo c’è una piccola fetta di siti che salvano le password in chiaro (o addirittura usano la crittazione al posto dell’hashing), ma la cultura della sicurezza fortuntamente si diffonde velocemente.

Le password devono assolutamente essere salvate come hash

Partiamo come sempre con un esempio: visito il sito X e decido di registrarmi. Nel form per la registrazione inserisco username, email e password, le solite cose insomma.

Un sito moderno prende la mia password, calcola il suo hash e salva quell’hash all’interno del database. Se io ho scelto la password “pippo” e l’algoritmo scelto dallo sviluppatore del sito è l’MD5, allora nel database dovrò salvare l’hash MD5 di pippo, che è 0c88028bf3aa6a6a143ed846f2be1ea4

Il tempo passa e decido di tornare su quel sito, e quindi devo per forza effettuare il login. Inserisco username e password e premo invio. Che cosa fa il sito, in questo caso? Prende la password che ho inserito nel login e ne calcola l’hash MD5, e poi la compara con quella presente nel database.

I due hash coincidono? Allora ti permetto di loggarti

I due hash non coincidono? Allora sicuramente hai sbagliato password, e quindi ti nego il login

Il vantaggio di usare gli hash salta subito all’occhio: se un malintenzionato riesce ad accedere al mio database, troverà solo gli hash delle password e non le password in chiaro. Dato che gli hash, come dovresti ricordarti, sono irreversibili, per il malintenzionato è impossibile risalire alle password.

Quando io entro su Gmail e inserisco la mia password, Google la trasforma nell’hash che vi ho detto sopra e compara i due codici. Se sono uguali, mi fa entrare. Dato che non c’è modo di risalire alla password, conoscendo l’hash, ve l’ho pubblicato senza problemi. Chiaro il concetto?

Troppo bello per essere vero?

Beh, si e no. Si, perché tutt’ora viene usato il sistema dell’hashing delle password (e non se conosce uno migliore in termini di funzionalità e sicurezza), ma anche No dato che ci sono alcuni problemi di sicurezza da tenere presente.

Come hai capito, se hai letto con attenzione, gli hash sono deterministici per cui l’input A fornirà sempre l’hash B. Questo significa che, volendo, è possibile generare una tabella dove io inserisco tutti gli input A possibili e ne calcolo il rispettivo hash B. (un pò come chiedere a un fabbro di creare tutte le chiavi possibili per tutte le serrature. E’ un pò faticoso ed è un processo lento, ma alla fine avrei tutte le chiavi del mondo!)

Questo genere di tabelle è chiamato rainbow tables, e sono generalmente usate dai cracker per riuscire a ricavare le password dalle funzioni di hash. Come ti ho detto, le funzioni di hash sono irreversibili da un punto di vista matematico, ma se io riuscissi a calcolare gli hash di tutte le parole del mondo, sarei in grado di decriptarle tutte, no?

Un esempio di rainbow table:

Password Hash MD5
mario de2f15d014d40b93578d255e6221fd60
pippo 0c88028bf3aa6a6a143ed846f2be1ea4
qwerty d8578edf8458ce06fbc5bb76a58c5ca4

Se dopo aver crackato un database trovassi l’hash d8578edf8458ce06fbc5bb76a58c5ca4 allora saprei già che l’utente ha usato la password qwerty.

Con le tabelle arcobaleno è uno degli attacchi possibili agli hash, ma i developer sono gente sveglia e quindi se ne sono usciti con una trovata geniale: sale e pepe. Si, hai capito bene, sale e pepe 🙂

Sale e pepe

Il concetto di salt e pepper è nato per rendere più sicuro il processo di hashing di una password e della sua successiva verifica (autenticazione). Dato che i computer moderni permettono di generare rainbow table molto grandi, che poi finiscono su internet, c’è stato bisogno di un sistema ingegnoso per evitare che gli hash fossero riconoscibili tramite attacchi così “vigliacchi” come quello delle tavole arcobaleno.

Partiamo con il sale!

Salt

Il problema che voglio risolvere è questo: voglio scrivere gli hash delle password all’interno del mio database, ma voglio evitare che un cracker le possa forzare grazie alle tabelle arcobaleno. Che cosa mi invento?

La genialata del salt è molto semplice: è sufficiente aggiungere una stringa casuale alla password dell’utente, in modo da rendere l’hash assolutamente unico.

Ipotizziamo che il cracker di turno abbia una rainbow table composta da tutte le password minuscole che arrivano fino a 12 caratteri. Io, da povero sfigato, uso la password “pippo” e quindi sicuramente sarei vulnerabile a un attacco. Se però lo svluppatore del sito ha aggiunto un “salt” alla mia password, questa diventa inattaccabile. Se per esempio il sito aggiunge automaticamente i caratteri “CIAOCIAOCIAOCIAO” davanti alla password, questa aumenta di 16 caratteri, e quindi la mia è stata hashata come “CIAOCIAOCIAOCIAOpippo”.

Dato che il cracker non ha una rainbow table così ampia (dato che servirebbero terabyte e terabyte per generarne una simile), per lui è impossibile decodificare l’hash di “CIAOCIAOCIAOCIAOpippo”.

Nota: l’utente non sa che è stato usato un salt sulla sua password e poco gli importa dato che per lui non cambia assolutamente nulla. In fase di autenticazione, infatti, il sito aggiunge automaticamente “CIAOCIAOCIAOCIAO” a tutti i tentativi di login, per cui se l’utente prova a loggarsi, il sito ragionerà così:

– l’utente ha inserito la password “pippo” ma io devo calcolare l’hash aggiungendo il sale, e quindi calcolo l’hash di “CIAOCIAOCIAOCIAOpippo”

L’hash di questa password “salata” è uguale all’hash che c’è nel database? Se si: permetto il login. Se no: non permetto il login.

Lo scopo del sale è quello di impedire l’utilizzo automatico di tabelle precompilate (le rainbow). Il sale NON ha lo scopo di rendere una password più lunga o più complessa (chiariamo bene questo concetto!). Dato che il salt non deve rendere la password più sicura, può essere tranquillamente inserito nel database. Il salt non deve essere necessariamente nascosto!

Se il cracker arriva sul database e vede che l’utente A ha un hash B che è stato formato aggiungendo il sale “QCDJASFFUSAGFAYTIUIGHSAUDHAISDHIHDASDUHASIDABSIDVASDVISDVAISVDA”, per lui diventa impossibile usare una rainbow table e quindi il nostro scopo è stato raggiunto. Per riuscire a decriptare l’hash di quello specifico utente, dovrebbe computare una rainbow table così:

QCDJASFFUSAGFAYTIUIGHSAUDHAISDHIHDASDUHASIDABSIDVASDVISDVAISVDAa

QCDJASFFUSAGFAYTIUIGHSAUDHAISDHIHDASDUHASIDABSIDVASDVISDVAISVDAb

QCDJASFFUSAGFAYTIUIGHSAUDHAISDHIHDASDUHASIDABSIDVASDVISDVAISVDAc

……

QCDJASFFUSAGFAYTIUIGHSAUDHAISDHIHDASDUHASIDABSIDVASDVISDVAISVDAzzzzzzzzv

QCDJASFFUSAGFAYTIUIGHSAUDHAISDHIHDASDUHASIDABSIDVASDVISDVAISVDAzzzzzzzzz

In pratica dovrebbe generare una rainbow table solo per riuscire a attaccare quello specifico utente di quel sito specifico. Se la tua password vale milioni di dollari, allora un pazzo potrebbe anche mettersi a fare una cosa del genere ma nel 99,99% è impossibile che qualcuno si metta a genere le rainbow table con i salt (anche perché se io domani cambiassi il salt di tutti i miei utenti, la rainbow table che il cracker ha generato diventa immediatamente inutile, e dovrebbe rifarne un’altra 😀 )

Differenza tra sale e pepe

In sostanza, sono la stessa cosa: sono entrambe delle stringhe che vengono aggiunte a una password per migliorarne la sicurezza (come abbiamo visto, sono molto efficaci nel bloccare gli attacchi con tavole arcobaleno).

Il salt è una stringa univoca che viene generata/usata per una singola password. Ad esempio un salt può generato ogni volta che un utente si registra e lo si assegna a lui. Ogni utente, quindi, ha il suo specifico salt con cui è stata hashata la password. Come ho già detto, il salt in questione può essere scritto tranquillamente sul database dato che il suo scopo non è migliorare la password, ma impedirne la decodifica.

Il pepper è una stringa univoca che viene usata per tutte le password e che generalmente non viene custodita nel database. Se io ho un file .php all’interno del mio FTP in cui c’è un codice univoco tipo “CIAO”, posso concatenarlo alle password per migliorare l’hash. Questo “pepe” viene aggiunto a tutte le password e quindi è “consigliato” tenerlo segreto. Se questo codice viene inserito in un file, anziché nel database, la protezione è doppia dato che un cracker dovrebbe riuscire a bucare sia l’FTP che il database (e poi buona fortuna a crackare gli hash :D)

Se devo sviluppare un sito e gestire le password, che consiglio mi dai?

Per prima cosa devi valutare l’importanza del sito che stai sviluppando. Per siti di “media entità”, può addirittura bastare un hashing semplice (a patto di usare un algoritmo moderno, vedi più in basso). Se invece stai sviluppando un sito che richiede ottime misure di sicurezza, allora potresti optare per la soluzione “definitiva” per lo storage delle password:

Il modo migliore, attualmente, per mantenere sicure le password è quello di custodire gli hash (generati con algoritmi moderni come SHA512) a cui è stato aggiunto un salt univoco per ogni utente e un pepper generale che è custodito segretamente in un file esterno.

Questa configurazione garantisce un ottima protezione anche nel caso in cui il database e l’FTP venissero bucati. Se le stringhe scelte per il sale e pepe hanno una notevole entropia, è sostanzialmente irrealistico che qualcuno si metta a crackarle con i sistemi tradizionali (rainbow & co).

Un altro modo di usare gli hash: verifica di integrità

Ti ho spiegato come vengono usati gli hash per gestire le password e l’autenticazione degli utenti, ma c’è un altro campo in cui queste funzioni vengono usate. Sto parlando della verifica dell’integrità. (in questo caso, più che hash dovremmo parlare di checksum, ma per semplicità usiamo il termine generico)

Ti ricordi le proprietà degli hash? Ad esempio il fatto che un determinato input produce sempre lo stesso hash e che una piccola variazione dell’input produce una variazione enorme nell’hash? (effetto valanga)

Queste due proprietà tornano molto utili quando c’è la necessità di verificare che un messaggio, software, o qualsiasi altra cosa non sia stato manipolato da nessuno.

Partiamo con un esempio: io sono un programmatore che sviluppa software open source. Ho paura che qualche malintenzionato stia distribuendo il mio software al quale ha aggiunto dei virus o altro codice malevolo.

Come faccio per proteggere i miei preziosi utenti?

Posso fare questo: prendo il codice sorgente del mio software e ne estrapolo l’hash. Poi dico a tutti gli utenti: we, ragassuoli, l’hash del mio software è questo 530a2968604a4ca7f9c7d7e2b78e88f9. Prima di usare il software che avete scaricato, calcolate l’hash del codice e se non combacia con questo, attenzione, è stato manomesso!

Dato che gli hash godono dell’effetto valanga, se un cracker malintenzionato ha aggiunto qualcosa al codice, anche una sola virgola, l’hash finale sarebbe totalmente differente! Il controllo degli hash, quindi, può essere utilizzato per verificare l’integrità di qualsiasi cosa.

Forse non ci avrai mai fatto caso, ma tantissimi siti pubblicano gli hash del proprio software così da poter verificare l’integrita. Esempio:

hash open office

 Cliccando sul primo link MD5, infatti, OpenOffice.org ci dice che 236f8601418efed2dab2599835d03934 è l’hash di Apache_OpenOffice_4.1.0_Win_x86_install_it.exe

Gli hash moderni

Come hai visto, le funzioni di hash sono numerose (spesso ho nominato MD5 e SHA che sono i più famosi) ma in realtà ce ne sono a decine. Le differenze tra gli algoritmi sono tante: lunghezza dell’output, resistenza alla preimmagine, quantità di collisioni e così via.

Spulciare tutti gli algoritmi richiederebbe un post a sé, però ci tengo a precisare che l’hash MD5 è ormai considerato insicuro, dato che è stato usato per troppi e anni e ormai le tavole per la decrittazione sono presenti ovunque. E’ sufficiente copiare e incollare un hash MD5 su Google per trovare il corrispettivo.

La famiglia degli SHA è una delle più utilizzate a livello mondiale (sopratutto SHA-2 e SHA-512).

Nel caso specifico della memorizzazione delle password, esistono alcuni algoritmi moderni estremamente efficaci, che garantiscono livelli di sicurezza al top. Il più famoso, usato e consigliato è bcrypt, dato che ha una caratteristica peculiare: il processo di hashing avviene in più cicli (round) che possono essere modificati a piacere. In pratica si può dire a bcrypt di eseguire un maggior numero di cicli al fine di generare l’hash, e questo farà in modo che il processo di generazione dello stesso sia più lento.

E’ uno svantaggio, dite voi?

Beh si, dal lato dell’utente è uno svantaggio, dato che quando compila il form di registrazione, deve attendere qualche millisecondo in più, ma è un enorme vantaggio per la sicurezza. Immagina se il calcolo di un hash richiedesse 1 millisecondo mentre quelli con bcrypt (opportunamente settato) richiedessero 500 millisecondi. Un eventuale cracker che volesse generare una rainbow table per bcrypt impiegherebbe esattamente un tempo 500 volte maggiore rispetto a un hash tradizionale. Se io decidessi di incrementare il work-factor di bcrypt per portarlo a 1000 millisecondi, ecco che il tempo è raddoppiato rispetto a prima.

(l’utente, magari, dovrebbe aspettare un secondo in più in fase di registrazione [1 millisecondo -> 1000 millisecondi] ma se prima il cracker impiegava un mese per creare una determinata rainbow, ora ci metterebbe più di 80 anni)

Grazie a questa caratteristica, bcrypt può essere adeguato in base alla potenza dei computer che cresce di anno in anno, e quindi permette di rimanere sicuri senza dover cambiare ogni volta l’algoritmo.


Vuoi provare a “hashare” qualcosa? Prova questo tool online. Ti permette di inserire un testo qualsiasi e di scegliere l’algoritmo. Puoi trovare tutte le funzioni che ho nominato, e molte altre (MD5, SHA1, SHA512, ecc)

Danilo petrozzi

Ciao! Io sono Danilo Petrozzi, il fondatore di Eternal Curiosity. Oltre a essere un senior SEO Specialist e un Web Developer, è dall'età di 9 anni che mi appassiono a qualsiasi cosa ruoti intorno al web e all'informatica in generale.

Lascia un commento