Русский

Инструменты разработчика · SHA хеш-калькулятор

Тот же текст, разные SHA-256: новые строки, кодировки и скрытые байты

· Как это работает

ша-256 кодирование обработка текста отладка

Два одинаково выглядящих текстовых ввода, создающие разные дайджесты SHA-256, со скрытой новой строкой и выделенными байтами кодировки.
Оригинальная векторная иллюстрация ToolAcre

Командная строка говорит одно, а браузер говорит другое, хотя текст выглядит как один и тот же. Завершающие символы новой строки UTF-16 и CRLF объясняют почти каждый случай; в этом посте показано, как найти скрытые байты.

эхо говорит один хэш, инструмент говорит другой — повседневное несоответствие и почему ни один из них не является неправильным

Терминал сообщает об одном дайджесте SHA-256, а браузер сообщает о другом, похожем на идентичный текст. Инструмент командной строки не сломался, и браузер тоже. Хешируемые байты не одинаковы, хотя видимые символы выглядят одинаково. В этом посте прослеживаются наиболее распространенные источники этих скрытых байтов и показано, как найти их с помощью текстового поля и шестнадцатеричного средства просмотра.

Проблема почти никогда не заключается в самом алгоритме SHA-256. SHA-256 является детерминированным: одни и те же байты всегда создают один и тот же дайджест, и дайджест правильный. Когда выходные данные различаются, байты различаются. Путаница возникает потому, что «один и тот же текст» неоднозначен: человек видит символы, а хеш-функция видит байты, и в переводе между ними скрываются скрытые различия.

Завершающая новая строка — как echo добавляет байт, а printf — нет, и что это делает с дайджестом

Команда echo в оболочке добавляет к своему выводу символ новой строки (U+000A, байт 0x0A). Это сделано специально: соглашение в Unix, согласно которому текстовые файлы заканчиваются новой строкой, дает echo одно простое задание. Когда вы вводите echo abc в терминал и передаете его в sha256sum, обработанные байты имеют вид 61 62 63 0A (коды ASCII для a, b, c и байт для новой строки), а не 61 62 63. Инструмент ToolAcre, используемый для вычисления хешей, хеширует байты 61 62 63 и выдает другой результат.

Команда printf не добавляет новую строку, если вы не впишете ее в строку формата. печать abc | sha256sum вычисляет дайджест только байтов 61 62 63, что соответствует инструменту браузера. Вот почему сравнение хэшей часто означает запуск printf вместо echo, или передачу по конвейеру sha256sum с флагом -z, или указание необработанных входных данных в любой форме, которую предоставляет ваш инструмент. Скрытая новая строка — это самая распространенная причина разногласий между инструментами браузера и командной строки.

UTF-8 против UTF-16 — почему одни и те же символы являются разными байтами в некоторых оболочках и редакторах

UTF-8 и UTF-16 кодируют одни и те же символы как разные последовательности байтов. Символ é (U+00E9, е с острым ударением) кодируется двумя байтами UTF-8: 0xC3 0xA9. В UTF-16 (именно так JavaScript внутренне представляет строки) один и тот же символ занимает два байта в другом порядке (в зависимости от порядка байтов) или полностью в другой форме, если он состоит из базового символа и объединяющего знака. Когда вы копируете cafe из приложения Windows и вставляете его в хэш-инструмент браузера, байты, хешируемые этим инструментом, могут не совпадать с тем, что хэширует терминал Mac, поскольку по умолчанию системы используют разные кодировки или формы нормализации.

Инструмент хеширования ToolAcre явно преобразует текст в UTF-8 перед хешированием с помощью TextEncoder. Это та же самая кодировка, которую по умолчанию использует командная строка Unix. Исходный код в apps/dev/src/lib/base64.js показывает функцию textToBytes, вызывающую новый TextEncoder().encode(), что гарантирует UTF-8. Если другая система использует UTF-16 или Latin-1 или любую другую кодировку, создаваемые байты будут отличаться. Инструмент отображает количество байтов вместе с дайджестом, поэтому вставка cafe и сравнение с хешем командной строки покажет разное количество байтов, если кодировки различаются.

CRLF, BOM и нормализация — окончания строк, метки порядка байтов и составные и разложенные акценты как невидимые входные различия

CRLF (возврат каретки + перевод строки, байты 0x0D 0x0A) — это соглашение об окончании строки в Windows; LF (только перевод строки, байт 0x0A) — это соглашение Unix. Текстовый файл, который выглядит одинаково при открытии в текстовом редакторе, может иметь разные окончания строк, и эти байты являются частью входных данных для хеша. Файл, отредактированный в Windows и проверенный на соответствие SHA-256, вычисленному в системе Unix, не будет совпадать, если одна система преобразовала окончания строк, а другая — нет.

Метка порядка байтов (BOM, байты 0xEF 0xBB 0xBF для UTF-8) — это необязательная последовательность в начале файла, которая сигнализирует о кодировке. Некоторые редакторы добавляют это; некоторые инструменты удаляют его; некоторые игнорируют это. Если файл содержит BOM и вы хешируете его побайтно, байты BOM являются частью дайджеста. Если вы затем скопируете видимый текст (из которого средство просмотра скрывает BOM) в инструмент, который не добавляет BOM, дайджесты не будут совпадать. Формы нормализации текста (NFD вместо NFC для составных и разложенных акцентов) добавляют еще один уровень: одна и та же буква с диакритическим знаком может быть представлена ​​как один предварительно составленный символ или как базовый символ, за которым следует комбинированный диакритический знак, при этом последовательности байтов различны.

Рабочий пример — одна строка хешируется с переводом строки и без него, затем в двух кодировках, с отображением каждой разницы в байтах.

Первый метод диагностики: используйте инструмент шестнадцатеричного дампа или онлайн-конвертер, чтобы точно узнать, с какими байтами работают ваши инструменты. Вставьте текст в кодировщик Base64, закодируйте его, и вы получите текстовую запись байтов. Затем декодируйте base64 в командной строке с помощью base64 -d и передайте его в od -A x -t x1z, чтобы увидеть последовательность шестнадцатеричных байтов. Если байты совпадают, алгоритм верен; если они этого не сделают, разница будет видна.

Второй метод диагностики: используйте ToolAcre SHA хеш-калькулятор для хэширования все более длинных входных данных, начиная с одного символа. Добавьте новую строку (что означает ввод Enter внутри текстового поля), добавьте пробелы, добавьте тот же текст с escape-последовательностями UTF-16, если входные данные поступили из источника, отличного от ASCII. Следите за тем, чтобы дайджест менялся с каждым добавлением. Количество байтов, отображаемое рядом с дайджестом, показывает, сколько байтов хеширует инструмент, что значительно сужает поиск.

Шестнадцатеричный регистр и пробелы в выводе — различия чисто косметические.

Шестнадцатеричное представление дайджеста не учитывает регистр. Прописные и строчные буквы представляют одни и те же байты: A = 10, a = 10. Некоторые инструменты выдают прописные буквы, некоторые строчные, а некоторые допускают и то, и другое. Если один дайджест написан строчными буквами, а другой — прописными, это один и тот же дайджест. Пробелы в дайджесте носят чисто косметический характер. Дайджест, отображаемый как ba78 16bf и ba7816bf, одинаков; пространство — это просто выбор форматирования. Несоответствия, вызванные регистром или пробелами, не являются настоящими несоответствиями.

Различия в форматировании фиксированной ширины также невидимы на уровне байтов. Дайджест, показанный с помощью дефисов, пробелов или двоеточий (например, ba-78-16-bf), представляет собой соглашение о форматировании, которое облегчает чтение людьми, а не изменение фактических байтов. Инструмент ToolAcre всегда выдает строчные буквы без разделителей, и это формат, который печатает большинство инструментов командной строки. Если вы сравниваете с инструментом, который излучает по-другому, сначала преобразуйте его в то же представление.

Чего это не касается — хеширование файлов, к которым применяются те же принципы, но байты поступают с диска, а не из текстового поля.

Хэш-калькулятор ToolAcre SHA выполняет преобразование UTF-8 перед хешированием, отображает количество входных байтов и предлагает выходные данные в формате Base64 и шестнадцатеричной форме. В исходном файле apps/dev/src/lib/hash.js показана функция hashText, вызывающая DigestBytes, которая передает bytes.slice().buffer в crypto.subtle.digest. Комментарии в этом файле явно документируют, что шаг UTF-8 является намеренным, и отмечают разницу между различными кодировками. Проверка введенных вами данных по известному вектору (хэши abc до ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad в шестнадцатеричном формате) показывает, что инструмент браузера работает правильно; любое отклонение указывает на разницу в байтах во входных данных.

Хеширование файлов следует тому же принципу. Байты в файле имеют значение: разница в конце строки при экспорте из одной системы и импорте в другую может изменить каждый дайджест. Некоторые инструменты предлагают опции для обработки концов строк во время сравнения; другие хэшируют файл как есть. Для воспроизводимости важно знать, хэширует ли ваш инструмент файл как двоичный или сначала выполняет нормализацию текста.

Вывод: хэш-байты, а не текст — ToolAcre SHA хэш-калькулятор хеширует вставленные вами байты, поэтому сначала проверьте, что вы вставили.

Сравнение и проверка работают только тогда, когда вы хешируете одни и те же байты. Начните с подтверждения того, что вы хешируете один и тот же ввод: запустите echo -n (или printf) вместо echo, чтобы избежать новой строки, явно укажите кодировку UTF-8, если ваш инструмент это позволяет, убедитесь, что CRLF не был вставлен редактором или системной утилитой. Затем выполните хеширование с помощью калькулятора ToolAcre и инструмента командной строки одновременно. Если дайджесты совпадают, байты идентичны. Если это не так, используйте отображение количества байтов и метод шестнадцатеричного дампа, чтобы найти скрытую разницу.

Как только вы поймете, где разошлись байты, вы сможете выбрать, нормализовать ли их для сравнения. Некоторые контрольные суммы предназначены для проверки целостности файла в том виде, в котором он существует на диске, и в этом случае целью является побайтовое хеширование. Другие предназначены для проверки идентичности видимого содержимого, и в этом случае нормализация концов строк и кодирования верны. Ни то, ни другое не является неправильным; они отвечают на разные вопросы. Алгоритм SHA-256 всегда прав; вопрос только в том, просите ли вы хэшировать один и тот же ввод в обоих случаях.