開発者ツール · SHA ハッシュ計算ツール
同じテキスト、異なる SHA-256: 改行、エンコーディング、および隠しバイト
· 仕組み
しゃ-256 エンコード テキスト処理 デバッグ
同じテキストのように見えるものに対して、コマンド ラインは 1 つのことを言い、ブラウザは別のことを言います。末尾の改行、UTF-16、および CRLF は、ほぼすべてのケースを説明します。この投稿では、隠されたバイトを見つける方法を示します。
エコーは 1 つのハッシュを示し、ツールは別のハッシュを示します - 日常的な不一致と、どちらも間違っていない理由
端末は 1 つの SHA-256 ダイジェストを報告し、ブラウザは同一テキストのように見える別のダイジェストを報告します。コマンドライン ツールは壊れておらず、ブラウザも壊れていません。目に見える文字は同じに見えても、ハッシュされるバイトは同じではありません。この投稿では、これらの隠されたバイトの最も一般的なソースを追跡し、テキスト フィールドと 16 進ビューアを使用してそれらを見つける方法を示します。
問題が SHA-256 アルゴリズム自体にあることはほとんどありません。 SHA-256 は決定的です。同じバイトは常に同じダイジェストを生成し、ダイジェストは正しいです。出力が異なる場合、バイトも異なります。混乱が生じるのは、「同じテキスト」があいまいであるためです。人間には文字が見えますが、ハッシュ関数にはバイトが見え、それらの間の翻訳には隠れた違いが隠れています。
末尾の改行 — echo がバイトを追加し、printf が追加しない方法と、それがダイジェストに何を行うか
シェルの echo コマンドは、出力に改行文字 (U+000A、バイト 0x0A) を追加します。これは仕様によるものです。テキスト ファイルが改行で終わるという Unix の慣例により、echo には 1 つの単純なジョブが与えられます。端末に echo abc と入力し、それを sha256sum にパイプすると、ダイジェストされたバイトは、61 62 63 ではなく、61 62 63 0A (a、b、c の ASCII コード、および改行のバイト) になります。 ToolAcre がハッシュの計算に使用するツールは、バイト 61 62 63 をハッシュし、異なる結果を生成します。
printf コマンドは、フォーマット文字列に改行を書き込まない限り、改行を追加しません。プリントフabc | sha256sum は、ブラウザ ツールと一致するバイト 61 62 63 のみのダイジェストを計算します。このため、ハッシュの比較では、多くの場合、echo の代わりに printf を実行するか、-z フラグを使用して sha256sum にパイプするか、ツールが提供する形式で生の入力を指定する必要があります。非表示の改行は、ブラウザ ツールとコマンド ライン ツールが一致しない最も一般的な理由です。
UTF-8 と UTF-16 — 一部のシェルとエディターで同じ文字が異なるバイトになる理由
UTF-8 および UTF-16 は、同じ文字を異なるバイト シーケンスとしてエンコードします。文字 é (U+00E9、アキュートアクセント付きの e) は、2 つの UTF-8 バイト: 0xC3 0xA9 としてエンコードされます。 UTF-16 (JavaScript が内部的に文字列を表現する方法) では、同じ文字が異なる順序 (エンディアンに応じて) で 2 バイトを占有するか、基本文字と結合マークから構成される場合は完全に異なる形式になります。 Windows アプリケーションからカフェをコピーしてブラウザのハッシュ ツールに貼り付けると、システムがデフォルトで異なるエンコーディングまたは正規化形式を使用しているため、ツールがハッシュしたバイトが Mac 端末のハッシュと一致しない可能性があります。
ToolAcre ハッシュ ツールは、TextEncoder を介してハッシュする前に、テキストを明示的に UTF-8 に変換します。これは、Unix コマンドラインがデフォルトで使用するエンコーディングと同じです。 apps/dev/src/lib/base64.js のソース コードは、UTF-8 を保証する new TextEncoder().encode() を呼び出す関数 textToBytes を示しています。別のシステムが UTF-16、Latin-1、またはその他のエンコーディングを使用している場合、生成されるバイトは異なります。このツールは、ダイジェストと一緒にバイト数を表示します。そのため、エンコーディングが異なる場合、カフェを貼り付けてコマンドライン ハッシュと比較すると、異なるバイト数が表示されます。
CRLF、BOM、正規化 - 行末、バイトオーダーマーク、目に見えない入力の違いとしての合成アクセントと分解アクセント
CRLF (キャリッジ リターン + ライン フィード、バイト 0x0D 0x0A) は Windows の行末規則です。 LF (改行のみ、バイト 0x0A) は Unix の規則です。テキスト エディターで開くと同一に見えるテキスト ファイルでも、異なる行末を持つことがあり、それらのバイトはハッシュへの入力の一部になります。 Windows で編集され、Unix システムで計算された SHA-256 と照合されたファイルは、一方のシステムが行末を変換し、もう一方のシステムが行末を変換していない場合には一致しません。
バイト オーダー マーク (BOM、UTF-8 のバイト 0xEF 0xBB 0xBF) は、エンコードを示すファイルの先頭にあるオプションのシーケンスです。一部の編集者はそれを追加します。いくつかのツールでそれを剥ぎ取ります。それを無視する人もいます。ファイルに BOM が含まれており、それをバイトごとにハッシュすると、BOM バイトはダイジェストの一部になります。次に、表示されているテキスト (ビューアが BOM を非表示にするテキスト) を、BOM を追加しないツールにコピーすると、ダイジェストは一致しません。テキスト正規化形式 (合成アクセントと分解アクセントの NFD と NFC) では、別の層が追加されます。同じアクセント文字は、単一の事前合成文字として、または基本文字とそれに続く結合アクセントとして表現でき、バイト シーケンスは異なります。
実用的な例 — 1 つの文字列を改行ありとなしでハッシュし、次に 2 つのエンコーディングで、各バイトの違いを示します
診断方法 1: 16 進ダンプ ツールまたはオンライン コンバータを使用して、ツールがどのバイトで動作しているかを正確に確認します。テキストを Base64 エンコーダに貼り付けてエンコードすると、バイトのテキスト記録が得られます。次に、コマンドラインでbase64 -dを使用してbase64をデコードし、それをod -A x -t x1zにパイプして16進バイトシーケンスを確認します。バイトが一致する場合、アルゴリズムは正しいです。そうでない場合は、違いが目に見えてわかります。
診断方法 2: ToolAcre SHA ハッシュ計算ツールを使用して、1 文字から始めて徐々に長い入力をハッシュします。改行を追加し (テキスト ボックス内に Enter を入力することを意味します)、スペースを追加し、入力が非 ASCII ソースからのものである場合は UTF-16 エスケープ シーケンスを使用して同じテキストを追加します。追加するたびにダイジェストが変化するのを観察してください。ダイジェストの横に表示されるバイト数は、ツールがハッシュしているバイト数を示し、検索範囲を大幅に絞り込みます。
出力内の 16 進数と空白 - 単なる表面的な違い
ダイジェストの 16 進表現では大文字と小文字が区別されません。大文字と小文字は両方とも同じバイトを表します: A = 10、a = 10。一部のツールは大文字を出力し、一部は小文字を出力し、一部はどちらも許可します。 1 つのダイジェストが小文字で、もう 1 つのダイジェストが大文字の場合、それらは同じダイジェストです。ダイジェスト表示内の空白は純粋に表面的なものです。 ba78 16bf と ba7816bf として表示されるダイジェストは同じです。スペースは単なる書式設定の選択です。大文字と小文字または空白によって引き起こされる不一致は、実際の不一致ではありません。
固定幅フォーマットの違いもバイト レベルでは見えません。ハイフン、スペース、またはコロンで表示されるダイジェスト (ba-78-16-bf など) は、人間が読みやすくするための書式設定規則であり、実際のバイトを変更するものではありません。 ToolAcre ツールは常に区切り文字なしで小文字を出力します。これは、ほとんどのコマンド ライン ツールが出力する形式です。放出方法が異なるツールと比較する場合は、最初に同じ表現に変換してください。
これでカバーされないもの — ファイルのハッシュ。同じ原則が適用されますが、バイトはテキスト フィールドではなくディスクから取得されます。
ToolAcre SHA ハッシュ計算ツールは、ハッシュ化の前に UTF-8 変換を実行し、入力バイト数を表示し、base64 および 16 進数の出力形式を提供します。ソース ファイル apps/dev/src/lib/hash.js は、bytes.slice().buffer を crypto.subtle.digest に渡す、digestBytes を呼び出す hashText 関数を示しています。そのファイル内のコメントでは、UTF-8 ステップが意図的であることが明示的に文書化されており、異なるエンコーディング間の違いが記載されています。既知のベクトル (ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad への 16 進数の abc ハッシュ) に対して入力をテストすると、ブラウザー ツールが正しく動作していることが確認されます。あらゆる偏差は、入力内のバイトの違いを示します。
ファイルのハッシュ化も同じ原則に従います。ファイル内のバイトが重要です。あるシステムからエクスポートし、別のシステムにインポートするときの行末の違いにより、すべてのダイジェストが変更される可能性があります。一部のツールには、比較中に行末を処理するオプションが用意されています。他の人はファイルをそのままハッシュします。再現性を確保するには、ツールがファイルをバイナリとしてハッシュするか、テキストの正規化を最初に実行するかを知ることが不可欠です。
要点: テキストではなくバイトをハッシュします — ToolAcre SHA ハッシュ計算ツールは貼り付けたバイトをハッシュするため、最初に貼り付けた内容を確認してください
比較と検証は、同じバイトをハッシュする場合にのみ機能します。まず、まったく同じ入力をハッシュしていることを確認します。改行を避けるために echo の代わりに echo -n (または printf) を実行し、ツールで許可されている場合は UTF-8 エンコーディングを明示的に指定し、エディターまたはシステム ユーティリティによって CRLF が挿入されていないことを確認します。次に、ToolAcre 計算ツールとコマンド ライン ツールを並べて使用してハッシュします。ダイジェストが一致する場合、バイトは同一です。一致しない場合は、バイト数表示と 16 進ダンプ方法を使用して隠れた違いを見つけます。
バイトがどこで分岐したかを理解したら、比較のためにバイトを正規化するかどうかを選択できます。一部のチェックサムは、ディスク上に存在するファイルの整合性を正確に検証することを目的としており、その場合はバイト単位のハッシュ化が目的です。他のものは、表示されるコンテンツが同じであることを検証することを目的としており、その場合、行末とエンコードの正規化が正しいことになります。どちらも間違いではありません。彼らはさまざまな質問に答えます。 SHA-256 アルゴリズムは常に正しいです。問題は、両方のケースで同じ入力をハッシュするように要求しているかどうかだけです。