francescomargiotta.com
  • Sicurezza
  • Backend

MD5, SHA-1, SHA-256: cosa significa davvero «rotto»

Collisioni, preimmagini, effetto valanga. Perché MD5 è inutilizzabile per le firme ma legittimo per le chiavi di cache, e perché SHA-256 è la scelta sbagliata per le password.

di Francesco Margiotta Casaluci6 min di lettura

MD5 è «rotto», SHA-1 è «rotto», usate SHA-256. È il riassunto che si trova ovunque, ed è troppo grossolano per essere utile: non dice in che senso siano rotti, non dice per quali usi lo siano davvero, e soprattutto porta alla conclusione sbagliata che SHA-256 sia la risposta giusta a qualunque domanda. Non lo è: per le password, SHA-256 è una scelta pessima quanto MD5.

Vale la pena separare le proprietà che una funzione di hash può avere, perché «rotto» significa cose diverse a seconda di quale proprietà è caduta.

Cosa fa una funzione di hash

Una funzione di hash crittografica prende un input di lunghezza qualsiasi e produce un output di lunghezza fissa. Deve essere deterministica — stesso input, stesso output, sempre — e veloce da calcolare in avanti.

input: "ciao"

MD5      (128 bit)  6e6bc4e49dd477ebc98ef4046c067b5f
SHA-1    (160 bit)  1e4e888ac66f8dd41e00c5a7ac36a32a9950d271
SHA-256  (256 bit)  b133a0c0e9bee3be20163d2ad31d6248db292aa6dcb1ee087a2aa50e0fc75ae2

La proprietà più visibile è l'effetto valanga: un cambiamento minimo nell'input deve rendere l'output completamente diverso, senza alcuna somiglianza residua.

SHA-256("ciao")  b133a0c0e9bee3be20163d2ad31d6248db292aa6dcb1ee087a2aa50e0fc75ae2
SHA-256("Ciao")  25c73520e69f4bf229811e8e46ffe7d80471544b9bee15ed25044b86be4115ad
Una sola lettera maiuscola di differenza: 60 dei 64 caratteri esadecimali cambiano.

Le tre proprietà, e quale cade per prima

Qui sta la distinzione che rende leggibile tutto il resto. Le garanzie di sicurezza sono tre, di forza decrescente, e cadono in ordine.

ProprietàSignificatoSe cade
Resistenza alle collisioniDifficile trovare due input qualsiasi con lo stesso hashLe firme digitali e i certificati non sono più affidabili
Resistenza alla seconda preimmagineDato un input, difficile trovarne un altro con lo stesso hashUn file specifico può essere sostituito con uno falso
Resistenza alla preimmagineDato un hash, difficile risalire a un input che lo producaL'hash smette di nascondere l'input

La resistenza alle collisioni è la più debole delle tre, per una ragione statistica: il paradosso del compleanno. Su un output di n bit servono circa 2^(n/2) tentativi per trovare una collisione, contro i 2^n necessari per invertire un hash. Per MD5 significa 2^64 invece di 2^128 — ed è il motivo per cui è la prima a cadere, sempre.

Lo stato degli algoritmi

AlgoritmoOutputStatoUso
MD5128 bitCollisioni pratiche dal 2004, anche a prefisso sceltoMai per la sicurezza
SHA-1160 bitCollisione pratica dimostrata nel 2017Mai per la sicurezza
SHA-256256 bitNessun attacco pratico notoScelta predefinita
SHA-512512 bitNessun attacco pratico notoPiù veloce di SHA-256 su CPU a 64 bit
SHA-3224–512 bitNessun attacco pratico notoCostruzione diversa, utile come alternativa
BLAKE3variabileNessun attacco pratico notoMolto veloce, parallelizzabile

Sulla caduta di MD5 vale la pena essere precisi, perché la cronologia spiega la differenza tra un attacco teorico e uno operativo. Le prime collisioni arrivano nel 2004. Nel 2008 un gruppo di ricercatori le usa per farsi emettere un certificato di autorità di certificazione fraudolento ma tecnicamente valido. Nel 2012 il malware Flame sfrutta una collisione a prefisso scelto su MD5 per firmare i propri componenti come se venissero da Microsoft, distribuendosi attraverso il canale di aggiornamento di Windows. Tra «è stata trovata una collisione» e «un attacco reale ha firmato codice malevolo» sono passati otto anni.

SHA-1 ha seguito la stessa traiettoria: attacchi teorici dal 2005, la prima collisione concreta nel 2017 con due PDF diversi che condividevano lo stesso digest, e nel 2020 la collisione a prefisso scelto, cioè quella che permette di colpire documenti mirati anziché costruirne due su misura.

L'errore più grave: hash veloci per le password

È qui che la sintesi «usate SHA-256» diventa un problema. Le funzioni di hash generiche sono progettate per essere velocissime, e la velocità è esattamente la proprietà sbagliata quando l'input da proteggere è una password.

Se un database viene sottratto, l'attaccante non deve invertire l'hash: gli basta calcolare l'hash di miliardi di password candidate e confrontare. Una scheda grafica di fascia consumer prova nell'ordine di decine di miliardi di SHA-256 al secondo. Una password da otto caratteri casuali resiste ore. Una password scelta da una persona, molto meno.

FunzioneTentativi al secondo (ordine di grandezza)Adatta alle password
MD510¹¹No
SHA-25610¹⁰No
bcrypt (costo 12)10⁴
scrypt / Argon2id (parametri adeguati)10³–10⁴Sì, preferibili

Dove MD5 resta legittimo

Questa parte viene di solito omessa, ma è utile per non sprecare tempo a sostituire MD5 dove non serve. Quando non c'è un avversario che può scegliere l'input, MD5 è semplicemente una funzione di hash veloce a 128 bit.

  • Chiavi di cache e deduplicazione interna, dove gli input li generate voi.
  • Verifica di trasferimenti contro corruzione accidentale, non contro manomissione.
  • Partizionamento e distribuzione di carichi su bucket.
  • ETag HTTP, dove la funzione serve a rilevare un cambiamento, non a garantirne l'origine.

Il confine è netto: se qualcuno può guadagnare qualcosa fornendovi un input costruito ad arte, MD5 non va bene. In tutti gli altri casi la scelta è di prestazioni, non di sicurezza. Detto questo, per una funzionalità nuova non c'è ragione di scegliere MD5: BLAKE3 è più veloce e non porta con sé l'obbligo di spiegare a ogni revisione perché lo state usando.

Integrità autenticata: serve HMAC

Un errore ricorrente è concatenare una chiave segreta al messaggio e calcolarne l'hash, sperando di ottenere un codice di autenticazione. Con SHA-256 questo schema è vulnerabile all'attacco di estensione di lunghezza: la costruzione Merkle–Damgård su cui SHA-256 si basa espone lo stato interno nel digest, e questo permette a un attaccante di continuare il calcolo e produrre un digest valido per un messaggio più lungo, senza conoscere la chiave.

Vulnerabile:  SHA256(chiave ‖ messaggio)
Corretto:     HMAC-SHA256(chiave, messaggio)

HMAC-SHA256("chiave-segreta", "ciao")
  c9b5740a9e60f9f8cef49b4de3dabc25e5908962e6901a87c4bb9e9955c5d6a9

HMAC applica la funzione di hash due volte con due derivazioni distinte della chiave, e questo neutralizza l'estensione di lunghezza. È una primitiva standard, presente in ogni libreria crittografica: non c'è alcuna ragione di improvvisare un'alternativa. Vale la pena notare che SHA-3 e BLAKE2/3 non sono soggetti a questo attacco per costruzione — ma anche con quelli, per autenticare un messaggio si usa la primitiva pensata per farlo.

  • Integrità di file e firme digitali: SHA-256, o BLAKE3 se la velocità conta.
  • Password: Argon2id, altrimenti scrypt o bcrypt. Mai un hash generico, per quanto moderno.
  • Autenticazione di messaggi: HMAC-SHA256, mai la concatenazione fatta a mano.
  • Identificatori casuali: non usate un hash, usate un generatore crittografico di numeri casuali.
  • MD5 e SHA-1: solo dove un avversario non può scegliere l'input, e senza introdurne di nuovi.

L'autore

Francesco Margiotta Casaluci è backend engineer: progetta e sviluppa microservizi in Java e Spring Boot, pipeline dati e piattaforme cloud-native. Scrive di quello che implementa, e implementa gli strumenti gratuiti pubblicati su questo sito.

Leggi il profilo completo

Articoli correlati