Italiano

Strumenti per sviluppatori · SHA calcolatore hash

Stesso testo, diverso SHA-256: nuove righe, codifiche e byte nascosti

· Come funziona

sha-256 codifica elaborazione del testo debug

Due input di testo dall'aspetto identico che producono diversi SHA-256 digest, con newline nascosti e byte di codifica evidenziati
Illustrazione vettoriale originale ToolAcre

La riga di comando dice una cosa e il browser ne dice un'altra per quello che sembra lo stesso testo. I ritorni a capo finali, UTF-16 e CRLF spiegano quasi ogni caso; questo post mostra come trovare i byte nascosti.

echo dice un hash, lo strumento ne dice un altro: la mancata corrispondenza quotidiana e il motivo per cui nessuno dei due è sbagliato

Un terminale segnala un digest SHA-256 e un browser ne segnala un altro per quello che sembra essere un testo identico. Lo strumento da riga di comando non si è rotto e nemmeno il browser. I byte sottoposti ad hashing non sono gli stessi, anche se i caratteri visibili sembrano identici. Questo post traccia le fonti più comuni di quei byte nascosti e mostra come trovarli con un campo di testo e un visualizzatore esadecimale.

Il problema non è quasi mai l'algoritmo SHA-256 stesso. SHA-256 è deterministico: gli stessi byte producono sempre lo stesso digest e il digest è corretto. Quando le uscite differiscono, i byte differiscono. La confusione nasce perché "lo stesso testo" è ambiguo: una persona vede i caratteri, ma una funzione hash vede i byte, e la traduzione tra loro è dove si nascondono le differenze nascoste.

Il ritorno a capo finale: come echo aggiunge un byte e printf no, e cosa fa a un digest

Il comando echo in una shell aggiunge un carattere di nuova riga (U+000A, byte 0x0A) al suo output. Questo è dovuto alla progettazione: la convenzione in Unix secondo cui i file di testo terminano con un ritorno a capo dà a echo un lavoro semplice. Quando digiti echo abc in un terminale e lo colleghi a sha256sum, i byte digeriti sono 61 62 63 0A (i codici ASCII per a, b, c e il byte per il fine riga), non 61 62 63. Lo strumento ToolAcre utilizza per calcolare gli hash esegue l'hashing dei byte 61 62 63 e produce un risultato diverso.

Il comando printf non aggiunge una nuova riga a meno che non ne venga scritta una nella stringa di formato. printf abc | sha256sum calcola solo il digest di byte 61 62 63, che corrisponde allo strumento browser. Questo è il motivo per cui confrontare gli hash spesso significa eseguire printf anziché echo, oppure eseguire il reindirizzamento a sha256sum con il flag -z o specificare l'input non elaborato in qualunque forma fornita dallo strumento. Il ritorno a capo nascosto è il motivo più comune per cui uno strumento del browser e uno strumento da riga di comando non sono d'accordo.

UTF-8 contro UTF-16 — perché gli stessi caratteri sono byte diversi in alcune shell ed editor

UTF-8 e UTF-16 codificano gli stessi caratteri come sequenze di byte diverse. Il carattere é (U+00E9, una e con accento acuto) codifica come due UTF-8 byte: 0xC3 0xA9. In UTF-16, che è il modo in cui JavaScript rappresenta internamente le stringhe, lo stesso carattere occupa due byte in un ordine diverso (a seconda dell'endianness) o in una forma completamente diversa se composto da un carattere di base e un segno di combinazione. Quando copi café da un'applicazione Windows e lo incolli in uno strumento di hash del browser, i byte sottoposti a hash dello strumento potrebbero non corrispondere a quelli di un terminale Mac, perché i sistemi utilizzano per impostazione predefinita codifiche o moduli di normalizzazione diversi.

Lo strumento hash ToolAcre converte esplicitamente il testo in UTF-8 prima dell'hashing tramite TextEncoder. Questa è la stessa codifica utilizzata dalla riga di comando Unix per impostazione predefinita. Il codice sorgente in apps/dev/src/lib/base64.js mostra la funzione textToBytes che chiama new TextEncoder().encode(), che garantisce UTF-8. Se un altro sistema utilizza UTF-16 o Latin-1 o qualsiasi altra codifica, i byte prodotti saranno diversi. Lo strumento mostra il conteggio dei byte insieme al digest, motivo per cui incollare café e confrontare con un hash della riga di comando mostrerà conteggi di byte diversi se le codifiche divergono.

CRLF, BOM e normalizzazione: terminazioni di riga, contrassegni dell'ordine dei byte e accenti composti rispetto a quelli scomposti come differenze di input invisibili

CRLF (ritorno a capo + avanzamento riga, byte 0x0D 0x0A) è la convenzione di fine riga su Windows; LF (line feed alone, byte 0x0A) è la convenzione Unix. Un file di testo che sembra identico quando viene aperto in un editor di testo può contenere terminazioni di riga diverse e tali byte fanno parte dell'input dell'hash. Un file modificato su Windows e confrontato con un SHA-256 calcolato su un sistema Unix non corrisponderà se un sistema ha convertito le terminazioni di riga e l'altro no.

Il contrassegno dell'ordine dei byte (BOM, byte 0xEF 0xBB 0xBF per UTF-8) è una sequenza opzionale all'inizio di un file che segnala la codifica. Alcuni redattori lo aggiungono; alcuni strumenti lo rimuovono; alcuni lo ignorano. Se un file contiene un BOM e lo esegui l'hashing byte per byte, i byte BOM fanno parte del digest. Se quindi copi il testo visibile (da cui un visualizzatore nasconde BOM) in uno strumento che non aggiunge BOM, i digest non corrisponderanno. I moduli di normalizzazione del testo (NFD contro NFC per accenti composti e scomposti) aggiungono un altro livello: la stessa lettera accentata può essere rappresentata come un singolo carattere precomposto o come un carattere di base seguito da un accento combinato e le sequenze di byte sono diverse.

Esempio realizzato: una stringa sottoposta ad hashing con e senza un ritorno a capo, quindi in due codifiche, con ogni differenza di byte mostrata

Metodo diagnostico uno: utilizzare uno strumento di dump esadecimale o un convertitore online per vedere esattamente su quali byte stanno operando i tuoi strumenti. Incolla il testo in un codificatore base64, codificalo e avrai un record testuale dei byte. Quindi decodifica base64 sulla riga di comando con base64 -d e collegalo a od -A x -t x1z per vedere la sequenza di byte esadecimali. Se i byte corrispondono, l'algoritmo è corretto; in caso contrario, la differenza sarà visibile.

Metodo diagnostico due: utilizzare il calcolatore hash ToolAcre SHA per eseguire l'hashing di input progressivamente più lunghi, iniziando con un singolo carattere. Aggiungi una nuova riga (che significa digitare Invio all'interno della casella di testo), aggiungi spazi, aggiungi lo stesso testo con sequenze di escape UTF-16 se l'input proviene da una fonte nonASCII. Guarda il digest cambiare con ogni aggiunta. Il conteggio dei byte visualizzato accanto al digest indica quanti byte lo strumento sta sottoponendo ad hashing, il che restringe notevolmente la ricerca.

Maiuscole esadecimali e spazi bianchi nell'output: le differenze sono puramente estetiche

La rappresentazione esadecimale di un digest non fa distinzione tra maiuscole e minuscole. Le lettere maiuscole e minuscole rappresentano entrambe gli stessi byte: A = 10, a = 10. Alcuni strumenti emettono lettere maiuscole, altri minuscole, altri le consentono entrambe. Se un digest è minuscolo e un altro è maiuscolo, sono lo stesso digest. Gli spazi bianchi nella visualizzazione del riepilogo sono puramente estetici. Un digest visualizzato come ba78 16bf rispetto a ba7816bf è lo stesso; lo spazio è solo una scelta di formattazione. Le discrepanze causate da maiuscole e minuscole o da spazi bianchi non sono vere e proprie discrepanze.

Anche le differenze di formattazione a larghezza fissa sono invisibili a livello di byte. Un digest mostrato con trattini, spazi o due punti (come ba-78-16-bf) è una convenzione di formattazione che semplifica la lettura per gli esseri umani, non una modifica ai byte effettivi. Lo strumento ToolAcre produce sempre lettere minuscole senza separatori, che è il formato stampato dalla maggior parte degli strumenti da riga di comando. Se stai confrontando con uno strumento che emette in modo diverso, converti prima nella stessa rappresentazione.

Ciò che questo non copre: hashing dei file, dove si applicano gli stessi principi ma i byte provengono dal disco anziché da un campo di testo

Il calcolatore hash ToolAcre SHA esegue la conversione UTF-8 prima dell'hashing, visualizza il conteggio dei byte di input e offre moduli di output base64 ed esadecimali. Il file sorgente apps/dev/src/lib/hash.js mostra la funzione hashText che chiama digestBytes, che passa bytes.slice().buffer a crypto.subtle.digest. I commenti in quel file documentano esplicitamente che il passaggio UTF-8 è intenzionale e notano la differenza tra le diverse codifiche. Testare l'input rispetto al vettore noto (hash abc in ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad in formato esadecimale) stabilisce che lo strumento del browser funziona correttamente; qualsiasi deviazione indica una differenza di byte nell'input.

L'hashing dei file segue lo stesso principio. Ciò che conta sono i byte nel file: una differenza di fine riga quando esporti da un sistema e importi in un altro può modificare ogni digest. Alcuni strumenti offrono opzioni per gestire le terminazioni di riga durante il confronto; altri eseguono l'hashing del file così com'è. Sapere se il tuo strumento esegue l'hashing del file come binario o esegue prima la normalizzazione del testo è essenziale per la riproducibilità.

In conclusione: hash byte, non testo: il calcolatore di hash ToolAcre SHA esegue l'hash dei byte che incolli, quindi controlla prima cosa hai incollato

Il confronto e la verifica funzionano solo quando si esegue l'hashing degli stessi byte. Inizia confermando che stai effettuando l'hashing dello stesso identico input: esegui echo -n (o printf) invece di echo per evitare il ritorno a capo, specifica esplicitamente la codifica UTF-8 se il tuo strumento lo consente, controlla che CRLF non sia stato inserito da un editor o da un'utilità di sistema. Quindi esegui l'hash con la calcolatrice ToolAcre e lo strumento da riga di comando fianco a fianco. Se i digest corrispondono, i byte erano identici. In caso contrario, utilizzare la visualizzazione del conteggio dei byte e il metodo hex dump per trovare la differenza nascosta.

Una volta capito dove divergono i byte, puoi scegliere se normalizzarli per il confronto. Alcuni checksum hanno lo scopo di verificare l'integrità del file esattamente come esiste sul disco, nel qual caso l'obiettivo è l'hashing byte per byte. Altri hanno lo scopo di verificare che il contenuto visibile sia lo stesso, nel qual caso la normalizzazione delle terminazioni di riga e della codifica è corretta. Nessuno dei due è sbagliato; rispondono a domande diverse. L'algoritmo SHA-256 ha sempre ragione; la domanda è solo se gli stai chiedendo di eseguire l'hashing dello stesso input in entrambi i casi.