Le funzioni di hash sono delle funzioni che mappano una stringa binaria di lunghezza arbitraria ad una stringa binaria (tanti 1 e 0) a lunghezza fissa (eg. 256 bit). Come ben sappiamo, tutto (immagini, messaggi, programmi, ecc...) può essere rappresentato con una stringa binaria, i computer capiscono solo quello. Quindi una funzione di hash ci permettere di prendere qualsiasi cosa e di rappresentarlo con una stringa a di lunghezza fissa (eg. 256 bit corrispondono a 256/8=32 caratteri ASCII).
Chiaramente le stringhe di lunghezza arbitraria sono infinite, mentre le stringhe con una certa lunghezza sono finite. Questo vuol dire che ci sono tante (infinite) stringhe che vengono mappate sullo stesso hash. Se troviamo due stringhe che vengono mappate sullo stesso hash, diciamo di aver trovato una
collisione.
Una funzione di hash usata in crittografia deve avere le seguenti proprietà:
- dev'essere generabile velocemente: se voglio hashare un file di 4GB in una stringa a 128 bit, voglio metterci pochi secondi;
- dev'essere one-way: è facile calcolare un hash a partire da una stringa, è difficile (computazionalmente intrattabile) ritrovare la stringa a partire da un hash;
- ogni piccola modifica deve incidere molto sull'hash generato: l'hash di "aaaa" è totalmente diverso dall'hash di "aaab".
Hanno diversi usi in crittografia: firma digitale, verifiche di integrità, algoritmi di encryption, ecc...
Ovviamente, se vogliamo usare una funzione di hash per firmare o per criptare, non possiamo semplicemente applicare la funzione di hash. È possibile usare una funzione di hash per fare queste cose, ma l'applicazione della funzione è solo uno step per arrivare al risultato finale.
MD5, SHA, Whirlpool, BLAKE (ecc...) sono semplicemente alcuni algoritmi per calcolare degli hash. Come faccio a trasformare una stringa qualsiasi ad una stringa a lunghezza fissa con le proprietà che ho descritto prima? Applico uno di questi algoritmi.
Se vogliamo utilizzare una funzione di hash per memorizzare una password in un database. Se salvassimo direttamente le password, nel momento in cui qualcuno mi frega il database le credenziali di tutti gli utenti sarebbero compromesse.
Quindi decidiamo di memorizzare nel database solo l'hash della password: quando qualcuno prova a connettersi, io prendo l'hash della password che mi inviano e controllo se corrisponde all'hash che ho memorizzato. Visto che trovare una collisione (that is: un'altra password che viene mappata sullo stesso hash) è molto difficile, assumo che se gli hash corrispondono la password è esatta.
Abbiamo un altro problema: se pippo e pluto hanno la stessa password, nel database avrò memorizzato due hash identici. Se pippo mi frega il database, vede che pluto ha un hash uguale al suo e capisce la password. Per ovviare a questo problema su usa il salt (
@giupardeb), ho spiegato il suo funzionamento in questo post:
Criptare bene una password
Ne approfitto anche per spammare un po' la mia guida, dove (tra le altre cose) c'è spiegato come usare RSA e le funzioni di hash per effettuare la firma digitale:
[GUIDA] Cosa è l'algoritmo RSA