ハッシュ化
データを固定長の値に不可逆変換する処理。パスワード保存と改ざん検知の要。
概要
ハッシュ化とは、どんな長さのデータからでも固定長の値(ハッシュ値、ダイジェスト)を計算する処理です。1文字のテキストでも数GBの動画でも、たとえば SHA-256 なら必ず256ビットの値になります。重要な性質は2つ。同じ入力からは必ず同じハッシュ値が得られること、そしてハッシュ値から元のデータを復元することは事実上不可能であること(不可逆性)です。
「元に戻せないなら何の役に立つのか」と思うかもしれませんが、この性質こそが価値です。元データを知らなくても「2つのデータが同じかどうか」を検証できるからです。パスワードの照合、ファイルの改ざん検知、Git のコミット識別子、キャッシュのキー生成、データベースのインデックスの一種であるハッシュインデックス——ハッシュはシステムのあらゆる場所に顔を出す、最も応用範囲の広い基礎概念のひとつです。
なお、鍵があれば元に戻せる暗号化とはまったく別の技術です。この違いを混同すると、セキュリティ設計で致命的な間違いにつながります。
なぜ生まれたか
原点のひとつは「大量のデータから目的のものを高速に見つけたい」という探索の問題です。データを先頭から順に調べれば時間がかかりますが、データから計算した小さな値を格納場所の番地として使えば、一発で目的の場所にたどり着けます。これがハッシュテーブルで、1950年代から使われてきた計算機科学の古典的な発明です。
もうひとつの系譜がセキュリティです。ネットワークでファイルを受け取ったとき、途中で改ざんされていないことをどう確認するか。全データを別経路で照合するのは非現実的ですが、短いハッシュ値だけ比べれば十分です。さらに深刻だったのがパスワード保存の問題です。パスワードを平文でデータベースに保存すると、漏洩した瞬間に全ユーザーの認証情報がそのまま流出します。実際に大規模な平文パスワード漏洩事件は何度も起きており、「サーバ管理者ですら元のパスワードを知り得ない形で保存する」手段としてハッシュ化が標準になりました。こうした用途に耐える、意図的に衝突を作れない「暗号学的ハッシュ関数」が整備されていきました。
詳細
暗号学的ハッシュ関数の性質
セキュリティ用途のハッシュ関数には、単なる固定長変換以上の性質が求められます。ハッシュ値から元の入力を見つけられないこと(原像計算困難性)、同じハッシュ値になる別の入力のペアを作れないこと(衝突耐性)、そして入力が1ビットでも変わればハッシュ値が全面的に変わること(雪崩効果)です。代表的なアルゴリズムは SHA-256 をはじめとする SHA-2 ファミリと、後継の SHA-3 です。かつて広く使われた MD5 と SHA-1 は衝突を人為的に作る攻撃が実証されており、セキュリティ用途では使ってはいけません(チェックサム的な用途に残っているだけです)。
性質は手で触ると一番早く腑に落ちます。入力を1文字だけ変えて、出力の変わり方を見てください。
計算中…同じ入力からは必ず同じ値が出ます。逆に、この64桁から元の文字列を求める方法は知られていません。 「復元はできないが、一致の検証はできる」— これがパスワードの保存にハッシュ化が使われる理由です。
パスワード保存 — ハッシュ化が鉄則である理由
パスワードは暗号化ではなくハッシュ化して保存します。暗号化は鍵があれば復号できるため、鍵が漏れれば全パスワードが平文に戻ってしまう。ハッシュ化ならデータベースが丸ごと流出しても、そこにあるのは元に戻せないハッシュ値だけです。ログイン時の照合は「入力されたパスワードを同じ方法でハッシュ化し、保存値と比較する」ことで実現します。
登録時と照合時で同じハッシュ計算を通し、最後にハッシュ値どうしを比較する——2つの経路が比較の一点で合流する構造です。
ただし素朴に SHA-256 を1回かけるだけでは不十分です。攻撃者はよくあるパスワードのハッシュ値を事前に大量計算した表(レインボーテーブル)を持っているからです。対策の第一が「ソルト」で、ユーザーごとにランダムな値をパスワードに混ぜてからハッシュ化します。これで事前計算表が無効になり、同じパスワードのユーザーでもハッシュ値が異なるようになります。第二が「意図的に遅いハッシュ関数」を使うことです。bcrypt、scrypt、Argon2 といったパスワード専用の関数は、計算に時間とメモリを食うよう設計されており、GPU による総当たり攻撃のコストを跳ね上げます。現在の推奨は Argon2(または bcrypt)で、認証機能を自作するならこの選択がほぼ必須の前提知識です。
改ざん検知と識別子としてのハッシュ
ハッシュのもうひとつの主戦場が完全性(integrity)の検証です。ソフトウェア配布サイトがダウンロードファイルと一緒に SHA-256 値を掲載しているのは、受け取ったファイルが改ざん・破損していないかを利用者が検証できるようにするためです。TLS の内部でも、通信内容が途中で書き換えられていないことの確認にハッシュベースの仕組み(HMAC など)が使われています。また、電子署名は「文書全体ではなくハッシュ値に対して公開鍵暗号で署名する」構成になっており、ここでもハッシュが土台です。
「内容から一意な名前を作る」用途も広く使われています。Git のコミットIDはコミット内容のハッシュ値そのもので、内容が1文字でも違えば別のIDになります。CDN やビルドツールがファイル名に app.a3f8c1.js のようなハッシュを埋め込むのも、内容が変わったときだけキャッシュを確実に無効化するためです。
実務での落とし穴
よくある間違いは3つあります。1つ目は用途と関数のミスマッチで、パスワードに高速な SHA-256 を使ってしまう(遅い Argon2/bcrypt を使うべき)、逆に大量データの重複検知に遅い関数を使って性能を落とす、というものです。2つ目はソルトなし・全ユーザー共通ソルトでのパスワード保存。3つ目は「ハッシュ化しているから安全」という思い込みで、電話番号や短い文字列のように入力の候補空間が小さいデータは、全候補をハッシュ化して突き合わせれば実質的に逆算できてしまいます。ハッシュ化は万能の匿名化ではなく、何を守れて何を守れないかを理解して使う道具です。