Tutorials

3. Funzioni Hash

Le funzioni di Hash sono metodi crittografici non reversibili e senza chiave, con proprietà specifiche che le rendono fondamentali in molti ambiti della sicurezza informatica. ---

Le funzioni di Hash sono metodi crittografici non reversibili e senza chiave, con proprietà specifiche che le rendono fondamentali in molti ambiti della sicurezza informatica.


Caratteristiche Generali

Output a Lunghezza Fissa: Generano un output (detto anche digest) di lunghezza fissa, indipendentemente dalla lunghezza dell'input. Deterministico: Allo stesso input corrisponde sempre lo stesso output. Distribuzione Uniforme e Apparentemente Random: Gli output generati sono uniformemente distribuiti nello spazio dei possibili digest e sembrano casuali. Non Reversibilità (Resistenza alla Preimage): Non è possibile risalire all'input che ha generato un determinato output (se non ricorrendo alla forza bruta). Effetto Valanga (Avalanche Effect): Una minima modifica all'input genera una notevole modifica all'output. Esempio: cane (MD5) $\rightarrow$ 0165801c0cce07c7a8751949845c93d2 Cane (MD5) $\rightarrow$ 43e277df3fdb75461844e25d2ac37f04 Cani (MD5) $\rightarrow$ df2ed2bbf803cc057ca133c14c10e952 Resistenza alle Collisioni: La probabilità che due input diversi generino lo stesso output (collisioni) è molto bassa e teoricamente trascurabile per funzioni robuste. Dato un hash, non è possibile calcolare l'hash di un input leggermente differente (se non con forza bruta). Non è possibile determinare due input che generano lo stesso output (se non con forza bruta). Rapidità di Calcolo: Sono molto rapide da calcolare (algoritmi polinomiali). Schema Merkle-Damgard: Molte delle principali funzioni hash elaborano l'input dividendolo in blocchi di dimensione fissa.


Schema Merkle-Damgard

Questo schema è alla base di molte funzioni hash attuali e opera in blocchi: 1. L'input viene sottoposto a padding per raggiungere una dimensione multipla della dimensione del blocco. 2. La dimensione del blocco è tipicamente maggiore della dimensione dell'hash. 3. Il messaggio di input viene diviso in blocchi. Ogni blocco viene dato in input a una funzione di compressione che prende in input anche l'output dello stadio precedente. 4. Per il primo stadio, non essendoci uno stadio precedente, si utilizza un valore iniziale (vettore di inizializzazione) stabilito dall'algoritmo.


Algoritmi Hash Principali

Gli algoritmi hash più utilizzati sono MD5 e la famiglia SHA.

MD5 Estensione di MD4, produce un output di 128 bit. La probabilità di collisioni non è trascurabile. È stato dimostrato che è possibile pilotare la generazione dell'output. Utilizzo sconsigliato o relegato a situazioni in cui la problematica delle collisioni non è rilevante.

SHA-1 Creata dall'NSA, produce un output di 160 bit. Probabilità di collisione molto più bassa di MD5. È stata rilevata una collisione su GitHub nel marzo 2017. NIST ha deprecato SHA-1 a dicembre 2022.

SHA-2 Estensione di SHA-1, produce output di 256 o 512 bit.

SHA-3 Estensione di SHA-2, in cui la lunghezza dell'output è arbitraria.


Utilizzi delle Funzioni Hash

Le funzioni hash trovano impiego in diverse applicazioni cruciali per la sicurezza:

Memorizzazione di password Verifica dell'integrità dei dati Identificazione di file ed eseguibili (usato dagli antivirus) Message Authentication Code (MAC) Firma digitale


Memorizzazione di Password

Le password dovrebbero essere memorizzate in forma hashed, non in chiaro e raramente cifrate (salvo per password manager specifici). Il salvataggio dell'hash offre protezione in caso di data breach e rapidità nella verifica delle credenziali.

Limitazioni del Solo Hash

L'uso del solo hash, anche con algoritmi sicuri, non è sufficiente a causa di:
Identificazione di password uguali: Si può determinare se più utenti hanno la stessa password. Attacchi Rainbow Table: Tabelle precalcolate di associazioni password/hash possono essere usate per risalire alle password. Ricerca in database pubblici: Gli hash possono essere cercati in database di password comuni o rubate.

Soluzioni per la Memorizzazione Sicura delle Password

#### 1. Salt Un "salt" è un testo randomico concatenato alla password prima di calcolare l'hash. Può essere concatenato prima, dopo, o in maniera alternata alla password. Maggiore è la lunghezza del salt, maggiore è la sicurezza. Il salt non deve essere segreto e può essere memorizzato in chiaro sul database. Vantaggio: Due utenti con la stessa password avranno hash diversi se i salt sono diversi.

#### 2. Pepper Un "pepper" è un secondo fattore randomico, simile al salt, ma con alcune differenze: È unico e globale per tutte le password. Non è memorizzato nel database. È considerato un segreto e memorizzato in forma sicura. È soggetto a rotazione (come qualsiasi altra chiave crittografica).

Algoritmi di Password Hashing Specifici

#### a. Bcrypt Funzione di password-hashing progettata per aumentare il livello di sicurezza. Obbliga l'uso del salt. La velocità di esecuzione è configurabile per contrastare attacchi brute force, anche con l'aumento della potenza computazionale. Utilizza il cifrario a chiave simmetrica Blowfish. Genera output di 192 bit (24 bytes). È il metodo di hash predefinito in molte distribuzioni Unix e Linux.

Bcrypt e Blowfish: Blowfish è un cifrario a blocchi con alte prestazioni di cifratura/decifratura, ma basse prestazioni nel setup iniziale della chiave. Il setup della chiave può essere configurato per modificarne la velocità. Non sono note vulnerabilità o attacchi riusciti contro Blowfish. In bcrypt, Blowfish è usato in modalità ECB.

Come un cifrario simmetrico crea un hash: Il testo da cifrare (input di Blowfish) deve essere fisso $\rightarrow$ output di dimensione fissa. La password diventa un input della funzione di derivazione della chiave di cifratura $\rightarrow$ cambiando la password, cambia la chiave e quindi il ciphertext. Anche il salt e il fattore di costo sono input della funzione di derivazione della chiave.

Formato output Bcrypt: $2b$10$22DSgc/exdO2J2yfBRH14ebzrm7PnK.eMSqaWOhKRgeR66RaB4I2S $2b: versione $10: costo (fattore di costo) $22DSgc/exdO2J2yfBRH14ebzrm7PnK.eMSqaWOhKRgeR66RaB4I2S: salt (16 byte radix-64 encoded) e hash (23 byte radix-64 encoded).

Performance di Bcrypt: L'analisi delle performance evidenzia un andamento esponenziale all'aumentare del fattore di costo, rendendo gli attacchi sempre più onerosi.

#### b. Argon2 Vincitrice del Password Hashing Competition 2015. Utilizza internamente la funzione di hash Blake2 (digest variabile fino a 2^32 byte). Permette di impostare più parametri rispetto a bcrypt: Salt: da 8 a 2^32-1 byte (16 byte raccomandati per le password). Grado di parallelismo: da 1 a 2^32-1. Dimensione dell'output desiderato: da 4 a 2^32-1 byte. Quantità di memoria da utilizzare (in KBytes). Numero di iterazioni da eseguire. Hash type: argon2d, argon2i, argon2id.

Modalità Operative di Argon2: Argon2d: Computazionalmente più costosa, più robusta contro attacchi a forza bruta con hardware dedicato (GPU). Tuttavia, l'accesso alla memoria dipende dalla password, rendendolo vulnerabile ad attacchi side-channel. Argon2i: Elimina la possibilità di attacchi side-channel (accesso alla memoria uniforme), ma diminuisce la resistenza agli attacchi a forza bruta. Argon2id: Modalità ibrida che combina le caratteristiche di argon2i e argon2d. Prevede un primo passaggio di tipo argon2i e i successivi di tipo argon2d. È la modalità operativa da prediligere.


Verifica Integrità dei Dati

Calcolando l'hash di un file in due momenti diversi, è possibile stabilire: Se il file è stato modificato (hash differente). Se il file è rimasto invariato (hash coincidente).

Applicazioni Pratiche: Verifica dell'autenticità di un file: Acquisendo un file e il suo hash (preferibilmente tramite due canali separati) si può verificare l'integrità. Identificazione di un file: Confrontando l'hash di un file con un database di hash noti (indipendentemente dal nome), si può stabilire se è conosciuto (es. antivirus per identificare malware o per system hardening).


Aggiornamento di Hash di Password Legacy

Quando si deve migrare da un algoritmo di hash meno sicuro a uno più robusto, l'OWASP cheat sheet series offre diverse opzioni:

1. Memorizzare il nuovo hash al primo login: Al primo accesso dell'utente, ricalcolare e memorizzare la password con il nuovo algoritmo. Si può anche forzare un password reset. È la modalità più semplice ma meno sicura, in quanto gli hash legacy possono rimanere vulnerabili a lungo. 2. Invalidare tutte le password: Forzare tutti gli utenti a effettuare un password reset, memorizzando le nuove password con il nuovo algoritmo. Questa è la modalità con la peggiore user experience. 3. Convertire le password esistenti: Inizialmente, sostituire MD5(password) con bcrypt(MD5(password)). Al successivo login dell'utente, memorizzare direttamente bcrypt(password). Questo offre una transizione più graduale.


Message Authentication Code (MAC) - HMAC

I Message Authentication Codes (MAC) garantiscono l'integrità e l'autenticazione (certezza del mittente) di un messaggio. Le funzioni hash da sole verificano solo l'integrità, quindi necessitano di un'integrazione per il MAC. HMAC (Hash Message Authentication Code) (RFC 2104) è un approccio comune, standardizzato dal NIST. Utilizza una chiave segreta condivisa tra mittente e destinatario per l'autenticazione. Per la verifica dell'integrità può essere usata qualsiasi funzione hash.

Funzionamento di HMAC

HMAC prevede due stadi di hash con input differenti: 1. Primo stadio: L'input è composto dal messaggio concatenato (in testa) con la chiave segreta opportunamente elaborata. 2. Secondo stadio: L'input è composto dall'output del primo stadio concatenato (in testa) con la chiave segreta opportunamente elaborata.

Parametri: b: dimensione del blocco della funzione hash. n: dimensione in bit dell'output della funzione hash. M: Messaggio. K: Chiave segreta (lunghezza raccomandata > n; se lunghezza > b $\rightarrow$ K = H(K)).


Collisioni nelle Funzioni Hash

Teoricamente, per ogni funzione di hash, sarà sempre possibile identificare delle collisioni (due input differenti che generano lo stesso digest). Praticamente, la possibilità di identificarle dipende da:

Dimensione in bit dell'output ($b$): Maggiore è $b$, minore è la probabilità di collisioni ($2^b$ digest possibili). Distribuzione dei digest nello spazio degli output: Più uniforme è la distribuzione, minore è la probabilità di collisioni. Funzione di compressione utilizzata.

Tipologie di Attacchi basati su Collisioni

1. Collision Attack: Determinare due input con lo stesso digest, indipendentemente dall'hash, in meno di $2^{n/2}$ tentativi (paradosso del compleanno). Rischio: Mette a rischio le firme digitali, permettendo di sostituire un documento firmato con un altro che ha lo stesso digest. 2. Preimage Attack: Determinare un input che genera un digest specifico, senza conoscere l'input originale. Rischio: Compromette i sistemi di salvataggio delle password, permettendo di generare una nuova password con lo stesso digest di quella originale. 3. Second Preimage Attack: Determinare un input che genera un digest specifico, conoscendo l'input originale. Rischio: Compromette la verifica dell'autenticità dei file, potendo creare file che vengono riconosciuti come altri.

Il Paradosso del Compleanno e le Collisioni

Il "paradosso del compleanno" stima in circa $2^{b/2}$ i tentativi necessari per trovare due input con lo stesso digest per una funzione hash con output di $b$ bit. Formula Probabilità: $p = 1 - \frac{P(365, n)}{365^n}$ (per il problema del compleanno) Estensione alle Hash: $p = 1 - \frac{P(2^b, n)}{(2^b)^n}$ Stima dei Tentativi ($n$) per probabilità $p$: Per $p = 0.5 \rightarrow n \approx 1.1774 \times 2^{b/2}$ Per $p = 0.75 \rightarrow n \approx 1.6651 \times 2^{b/2}$

$b$ (bit)Hash Possibili ($2^b$)$n$ (per $p=0.5$)$n$ (per $p=0.75$)
1665536301426
32$4.3 \times 10^9$$77 \times 10^3$$110 \times 10^3$
128 (MD5)$3.4 \times 10^{38}$$2.2 \times 10^{19}$$3.1 \times 10^{19}$
160 (SHA1)$1.4 \times 10^{48}$$1.4 \times 10^{24}$$2.0 \times 10^{24}$
256 (SHA2)$1.2 \times 10^{77}$$4.0 \times 10^{38}$$5.7 \times 10^{38}$
512 (SHA2)$1.3 \times 10^{154}$$1.4 \times 10^{77}$$1.9 \times 10^{77}$

Il Caso MD5

MD5 non è più considerato sicuro a causa della sua vulnerabilità alle collisioni. 2005: Scoperto il primo algoritmo efficiente (Wang e Yu) per determinare una coppia di input con lo stesso digest (collision attack), molto più efficiente della forza bruta. Inizialmente su sequenze di 128 byte, poi ottimizzato per collisioni in pochi minuti, anche per 64 byte. Proprietà sfruttata: Se $MD5(X_1) = MD5(X_2)$ con $X_1 \ne X_2 \rightarrow MD5(X_1 || S) == MD5(X_2 || S)$. Questo permette di creare coppie di input arbitrari con lo stesso digest, che differiscono solo per i primi byte. Attacchi più avanzati: Possibilità di determinare blocchi $M_i, M_{i+1}, N_i, N_{i+1}$ tali che $f(f(s_i, M_i), M_{i+1}) = f(f(s_i, N_i), N_{i+1})$.

Esempi di Sfruttamento Reale delle Collisioni su MD5: Coppia di file Postscript con stesso hash. Coppia di certificati digitali X.509 con la stessa firma: Possono differire per la chiave pubblica o altri campi. Questo permette di impersonificare una CA intermedia per firmare certificati considerati attendibili dai browser (sfruttato dal malware Flame nel 2012). Coppia di file eseguibili con lo stesso hash: Utilizzando la tecnica del chosen prefix collision, è possibile determinare due suffissi $S_1$ e $S_2$ tali che $MD5(P_1 || S_1) == MD5(P_2 || S_2)$, dove $P_1$ e $P_2$ sono due input differenti e non necessariamente della stessa lunghezza. Questo permette di creare file "condizionali" con una parte iniziale diversa ma stesso hash (preambolo) e una parte finale identica che implementa comportamenti diversi in base al preambolo.


Cronostoria Crittoanalisi SHA-1

1995: Pubblicazione di SHA-1. 2005: Attacco di collisione teorico in $2^{69}$ operazioni. 2006-2011: Attacchi di collisione reali su versioni semplificate dell'algoritmo. 2015: Attacco di collisione reale in $2^{57}$ operazioni. 2017: Identical prefix collision in $2^{65}$ operazioni. 2019: Chosen prefix attack teorico in $2^{67}$ operazioni. 2020: Attacco di collisione reale in $2^{64}$ operazioni. 2022: NIST dichiara SHA-1 deprecato.