テキストとコード / ガイド

同じに見える文字列でハッシュ値が異なる理由

ハッシュはバイト列を要約し、人は文字を見ます。Unicode では、見た目が同じ文字列でも異なるバイト列で表せる場合があります。

同じ見た目でも異なる文字列

単語 Café には、合成形の é 1文字(U+00E9)を含めることも、e(U+0065)と結合アキュートアクセント(U+0301)を続けることもできます。多くのフォントでは似た姿に描かれます。UTF-8 では合成形の é は C3 A9、分解形の末尾は 65 CC 81 というバイト列です。SHA-256 は入力のバイト列をハッシュ化するため、人には同じ単語に見えても、2つの綴りは異なるダイジェストになります。これはハッシュの故障ではなく、見た目の比較とバイト単位の比較が食い違っているのです。予期しないダイジェストを調べるときは元の形式を保ってください。

合意した規則で正規化する

Unicode の NFC は合成できる文字列を正規形へまとめ、NFD はそれを分解します。二つのシステムが、UTF-8 へ符号化してハッシュ化する前に NFC へ正規化すると合意していれば、例に示す二通りの Café の綴りは同じ正規化済みバイト列になります。埋め込まれた NFC ツールで分解形の入力を試せます。操作の順序が大切です。文字を正規化し、その文字を UTF-8 のバイト列へ変え、それから正確なバイト列をハッシュ化してください。先にハッシュ化すると、等価な綴りを一致させるために必要な情報が失われます。参加者全員が同じ処理を行えるよう、データの取り決めに正規化形式を明記します。

合意された規則がある場合は、UTF-8 に符号化してハッシュ化する前に正規化します。
  1. Café:合成済みの é、または e と結合アクセント
  2. NFC → 合意した形にそろえる
  3. UTF-8 バイト列 → SHA-256 ダイジェスト

無条件に正規化しない

正規化によって元のテキストそのものが変わることがあります。互換正規化の NFKC と NFKD は、NFC と NFD より広い置換を行います。検索用インデックスには有用でも、法的な逐語記録、既存の文書に対する電子署名、バイト単位での保存を保証するシステムには適さない場合があります。大文字と小文字の同一視、前後の空白の削除、改行形式の変更は別の判断であり、NFC に自動的に含まれる処理ではありません。通信規約が元のバイト列を要求したり、別の正規化手順を定めたりすることもあります。値が一致しないと気づいてから任意に正規化をかけるのではなく、その規約に従ってください。

バイト列を比べて検証する

合成済みの é を含む短い文字列と、e に結合アクセントを続けた文字列を用意します。それぞれを UTF-8 バイト表示ツールで調べ、末尾が C3 A9 と 65 CC 81 になることを確かめます。正規化前の文字列を別々にハッシュ化すると、ダイジェストは異なるはずです。次に両方へ NFC を適用し、バイト列と SHA-256 ダイジェストを比較します。コピーした入力に紛れ込む空白、改行、見えない文字もハッシュ値を変えるため注意してください。同じ文字コードと同じアルゴリズムで比較し、SHA-256 の値を SHA-512 の値と直接照合しないでください。

必要な等価性を決める

ファイルの完全性を確かめるなら、元のバイト列をハッシュ化し、どのバイトの変化も検出することが通常の目的です。人が入力した名前の重複を調べる場合は、先に明文化した正規化規則を適用することが考えられますが、大文字小文字や言語ごとの扱いまで一つの Unicode 形式だけでは決まりません。パスワードや認証では専用の設計に従う必要があり、単純な SHA-256 だけでパスワードを保存するのは不十分です。求めるのがバイトの同一性か、正規化したテキストの等価性か、人が見て似ていることかを明示してください。見た目だけから変換ツールが目的を推定することはできません。

変換ツールを試す

分解されたアクセントのある単語を NFC に正規化します。

NFC は、カノニカル分解に続いて再構成を行い、共通の構成形式を生成します。ブラウザでローカルに変換します。

変換ツールを開く

その他のガイド

変換ガイド

ガイドを探す