Generare un hash SHA-1

Scrivi o incolla qualcosa e ottieni il suo SHA-1: quaranta caratteri esadecimali. È rotto per le firme e per i certificati dal 2017, e serve comunque tutti i giorni, perché Git lo usa per identificare gli oggetti e perché esistono interfacce che nessuno tocca da quindici anni. Questa pagina lo calcola per quel motivo, non perché sia una scelta consigliabile per qualcosa di nuovo.

Risultato

La risposta compare qui mentre scrivi.

  • Dove gira

    Non viene caricato nulla, perché non c’è nessun file: il calcolo avviene in questa pagina.

  • Nessuna coda, nessun account

    Risponde alla velocità della tua macchina e non chiede mai chi sei.

  • Tutte le volte che vuoi

    Non si conta né si limita nulla: rispondere di nuovo non ci costa niente.

Come funziona

  1. Incolla il testo nel campo.
  2. Copia i 40 caratteri esadecimali.
  3. Non è stato caricato niente.

Cosa si è rotto esattamente nel 2017

A febbraio 2017 un gruppo di ricercatori di Google e del CWI di Amsterdam ha pubblicato due PDF diversi con lo stesso SHA-1. Non era una dimostrazione teorica: erano due file scaricabili, con contenuti visibilmente diversi e la stessa impronta di quaranta caratteri.

Tre anni dopo è arrivata la parte che ha chiuso il discorso: una collisione a prefisso scelto, cioè la possibilità di partire da due inizi qualsiasi e farli convergere. È quella che rende falsificabile un certificato o una firma reale, ed è il motivo per cui i browser e le autorità di certificazione avevano già smesso di accettare SHA-1 prima ancora che uscisse.

Perché Git lo usa ancora

Git non usa lo SHA-1 come misura di sicurezza ma come indirizzo: l’identificatore di un oggetto è il suo contenuto, e serve a ritrovarlo e a garantire che una copia sia identica all’originale. In quel ruolo la collisione è un problema molto più stretto, perché l’avversario dovrebbe far accettare al progetto un file costruito apposta.

Nemmeno così è stato lasciato com’era. Dal 2017 Git usa una variante che rileva le costruzioni tipiche di un attacco a collisione e rifiuta quegli oggetti, e da tempo esiste il supporto per repository con SHA-256. La migrazione è lenta per un motivo puramente pratico: l’identificatore compare in messaggi di commit, in ticket, in script e in URL di tutto il mondo.

Uno schema rotto non si rattoppa

Applicare lo SHA-1 due volte, concatenarlo con un sale o accostarlo a un MD5 non ripristina niente. Le collisioni si costruiscono sulla struttura interna della funzione, e quella struttura resta la stessa: le varianti fatte in casa spostano il problema di un passo e non lo tolgono.

La cosa dannosa è che sembrano funzionare. Un doppio SHA-1 supera qualunque prova superficiale e dà a chi lo ha scritto la sensazione di aver risolto, che è precisamente ciò che impedisce la sostituzione vera. Dove la scelta è tua, la risposta è SHA-256; dove non lo è, l’unica strategia sensata è sapere che il vincolo è dell’altra parte e trattarlo come tale.

La forma corta, e quando collide

I sette caratteri con cui si abbrevia un commit non sono un hash a parte: sono l’inizio dello stesso valore da quaranta. Bastano finché il repository è piccolo, e smettono di bastare prima di quanto suggerisca l’intuizione, perché il numero di coppie possibili cresce con il quadrato del numero di oggetti.

Il kernel Linux ha superato quella soglia e usa da anni prefissi di dodici caratteri. Git allunga da solo la forma corta quando un prefisso diventa ambiguo, il che significa che un identificatore a sette caratteri scritto in un ticket tre anni fa può oggi non individuare più niente di preciso — un ottimo motivo per copiare il valore intero in qualsiasi documento destinato a durare.

Quaranta caratteri

Centosessanta bit sono quaranta caratteri esadecimali, indipendentemente dall’input. È il numero che permette di riconoscere a colpo d’occhio con cosa si ha a che fare, fra i trentadue di un MD5 e i sessantaquattro di uno SHA-256.

La cifra è anche la ragione per cui lo SHA-1 sopravvive in tanti schemi vecchi: centosessanta bit erano una dimensione comoda, un campo binario da venti byte, e parecchi formati la hanno scritta nella loro struttura. Cambiare algoritmo lì dentro non è sostituire una chiamata di funzione ma cambiare il formato, ed è per questo che non succede.

L’identificatore di un oggetto Git non è l’hash del file

È la sorpresa che coglie tutti almeno una volta: lo SHA-1 di un file e l’identificatore che Git gli attribuisce non coincidono. Git non fa l’hash del contenuto e basta, ma di un’intestazione seguita dal contenuto — la parola `blob`, uno spazio, la lunghezza in byte e un byte zero.

La conseguenza pratica è che non si può confrontare l’identificatore di un oggetto con il risultato di `sha1sum` sul file corrispondente e concludere qualcosa. Per ottenere il valore che Git userebbe esiste il comando apposito, `git hash-object`, e per confrontare il contenuto di due file conviene semplicemente confrontarne gli hash con lo stesso strumento da entrambe le parti.

L’a capo, di nuovo

Come per ogni funzione di hash, un byte in più cambia tutto. `echo testo | sha1sum` non dà lo stesso risultato di questa pagina con `testo` scritto nel campo, perché `echo` aggiunge un ritorno a capo. La differenza si risolve con `printf %s` o con `echo -n`.

Su Windows si aggiunge il caso del fine riga doppio: un file salvato con `CRLF` ha due byte dove uno in `LF` ne ha uno, per ogni riga. Su un file di cento righe sono cento byte di differenza, e l’hash è ovviamente un altro. Se stai confrontando lo stesso testo passato per due sistemi diversi, quella è la prima cosa da controllare.

Quando usarlo e quando no

Va usato quando qualcosa che non puoi cambiare lo pretende: un’interfaccia di un partner, un vecchio meccanismo di webhook, un identificatore di oggetto Git, un formato di file che lo ha scritto nella propria struttura. In quei casi calcolarlo è l’unica cosa da fare, e sapere che è rotto serve a valutare il rischio, non a evitare il compito.

Non va usato in niente di nuovo, e in particolare non va usato dove una collisione avrebbe conseguenze: firme, certificati, verifica di integrità contro qualcuno che abbia interesse a ingannarti, deduplicazione di contenuti che arrivano dall’esterno. Per tutti quei casi la risposta è SHA-256, che questo sito ha su una pagina a parte.

Anche un hash legacy si calcola in locale

Il testo che si porta a un generatore di SHA-1 è quasi sempre un valore reale: il corpo di un webhook da confrontare con una firma, una stringa di un sistema ereditato, un identificatore di cliente. Che l’algoritmo sia vecchio non rende meno delicato l’input.

Qui il conto avviene nella pagina, quindi non c’è nessuna comunicazione di quel testo verso di noi e niente da giustificare sul piano del GDPR. È anche il motivo per cui la pagina non conserva una cronologia dei valori calcolati: sarebbe comoda per due minuti e vorrebbe dire tenere da qualche parte esattamente quello che è meglio non tenere.

Generare un hash SHA-1: domande frequenti

Posso ancora usare lo SHA-1?

Solo dove qualcos’altro lo impone e la collisione non ha conseguenze: un identificatore di oggetto Git, un’interfaccia vecchia, un formato binario con un campo da venti byte. Per qualsiasi cosa nuova, e per qualsiasi cosa che assomigli a una firma o a una verifica di integrità contro un avversario, la risposta è SHA-256.

Se è rotto, perché Git lo usa?

Perché lì serve come indirizzo del contenuto e non come misura di sicurezza, e perché sostituirlo significa cambiare un identificatore che compare in commit, ticket, script e URL di mezzo mondo. Dal 2017 Git usa comunque una variante che riconosce le costruzioni tipiche di un attacco, e il supporto per SHA-256 esiste.

Applicarlo due volte o con un sale lo rende sicuro?

No. Le collisioni si costruiscono sulla struttura interna della funzione, che resta la stessa: le varianti fatte in casa spostano il problema senza toglierlo, e in più danno l’impressione che sia risolto. Se la scelta è tua, cambia algoritmo invece di rinforzare questo.

Perché non coincide con l’identificatore del mio commit?

Perché Git non fa l’hash del solo contenuto: davanti mette un’intestazione con la parola blob, la lunghezza in byte e un byte zero. Per ottenere il valore che userebbe Git c’è `git hash-object`. Anche l’a capo finale del file contribuisce, come sempre.

Il testo esce dalla mia macchina?

No. Il calcolo avviene nella pagina e niente viene caricato. Vale la pena dirlo proprio qui, perché lo SHA-1 si calcola quasi sempre su materiale di un sistema in produzione: il corpo di un webhook, una stringa firmata, un identificatore reale.

Altri strumenti