一方向ハッシュ関数の基礎から実務まで—衝突耐性・長さ拡張攻撃対策と安全なパスワード保存の実践ガイド

一方向ハッシュ関数とは何か

一方向ハッシュ関数(以下「ハッシュ関数」)は、任意長の入力データを固定長のビット列に変換する関数で、重要な性質として「計算は容易だが逆算は困難(=一方向性)」であることが要求されます。入力がわずかに違えば出力は大きく変わる(Avalanche効果)ため、元データの同一性検証、データ整合性、認証、暗号プロトコルなど幅広い用途で使われます。

ハッシュ関数に期待される主要な性質

  • 第一事前像耐性(Preimage resistance):あるハッシュ値 h が与えられたとき、そのハッシュ値を与える入力 m を見つけるのが計算上困難であること。
  • 第二事前像耐性(Second preimage resistance):ある m1 が与えられたとき、同じハッシュ値を持つ別の m2(m2 ≠ m1)を見つけるのが困難であること。
  • 衝突耐性(Collision resistance):任意の異なる m1, m2 の組で同じハッシュ値を持つペアを見つけるのが困難であること。出力長 n の場合、誕生日攻撃により理論的には約 2^(n/2) の試行で衝突が起きうる点に注意が必要です。
  • その他の実務上の性質:長さ拡張攻撃への耐性、鍵付きハッシュ(HMAC)での安全性、実装における定数時間比較など。

代表的な構造とその特徴

ハッシュ関数の内部構造として有名なのは Merkle–Damgård 構造とスポンジ構造です。前者はブロックごとに圧縮関数を適用する設計で、MD5・SHA-1・SHA-2 系列が該当しますが、長さ拡張攻撃に脆弱になる点が知られています。スポンジ構造は Keccak(SHA-3)の基盤で、長さ拡張攻撃に対して本質的に強い設計です。

主要なハッシュアルゴリズムと現在の評価

  • MD5:かつて広く使われたが、2004 年頃から衝突攻撃が現実的であることが示され、認証用途ではもはや不適切です。実際の攻撃(への応用)も報告されています。
  • SHA-1:構造上の弱点が指摘され、2017 年に Google と CWI による「SHAttered」プロジェクトで実用的な衝突が提示されました。新規のセキュリティ用途には不推奨です。
  • SHA-2(SHA-224/256/384/512):現在も広く安全とされるハッシュ族。出力長に応じて衝突耐性が変わるため用途に応じた選択が必要です。
  • SHA-3(Keccak):スポンジ構造を採用し、設計が SHA-2 とは独立しているため将来の多様な脅威に対する代替として有用です。
  • BLAKE2 / BLAKE3:高速で安全性の高いハッシュ関数。特に性能重視の用途で注目されています。

ハッシュの応用分野(実務的な使い方)

  • データ整合性検証:ダウンロードファイルやバックアップデータの整合性確認。
  • デジタル署名の要素:大きなメッセージをそのまま署名するのではなく、ハッシュ値を署名することで処理効率を高める。
  • 認証(HMAC):鍵付きハッシュ(HMAC)はメッセージ認証コード(MAC)として用いられ、長さ拡張攻撃から保護される。
  • パスワード保存:平文保存は危険。単純なハッシュではレインボーテーブルなどに弱いため、ソルトや鍵引伸ばし関数(KDF)を併用する必要がある。
  • ブロックチェーンやMerkle木:トランザクション集合の整合性や効率的な検証にハッシュが活用される。

パスワード保存の具体的注意点

パスワード保存で重要なのは、単に SHA-256 を使うだけでは不十分な点です。実務的な推奨は以下の通りです。

  • ユニークでランダムなソルトをユーザー毎に生成して保存する(例:16 バイト以上のランダム値)。
  • 遅延とメモリコストを提供する KDF を使う。現代では Argon2(特に Argon2id)が推奨される。bcrypt や scrypt、PBKDF2(レガシー互換性が必要な場合)も選択肢。
  • パラメータ(時間コスト、メモリコスト、並列度)は運用環境に合わせて決める。定期的に見直し、計算資源の進歩に合わせて引き上げる。
  • 入力の正規化(Unicode の正規化フォーム、エンコーディング)を行い、異なる表現の同一文字列が異なるハッシュになるのを防ぐ。
  • 比較は必ず定数時間比較で行い、タイミング攻撃を防ぐ。

代表的な攻撃と落とし穴

  • 衝突攻撃:出力長の平方根程度の計算量で発生する「誕生日攻撃」が一般原理。出力長を十分確保することが重要です(例:128 ビット衝突耐性では 256 ビット出力)。
  • 長さ拡張攻撃:Merkle–Damgård 構造のハッシュでは、ハッシュ値とメッセージ長さが分かるとハッシュ後に追加データを付けても新たな正当なハッシュを計算できる場合があります。鍵付き用途では HMAC を用いて回避します。
  • レインボーテーブル / 総当たり:ソルトと十分なコスト(認証試行制限)で防げます。GPU や ASIC に対して有効な遅延・メモリ消費を導入することが重要です。
  • アルゴリズムの陳腐化:MD5、SHA-1 のように将来的に実用攻撃が発見されうるため、アルゴリズムの選定と更新方針を持つ必要があります。

実装上のチェックリスト(現場向け)

  • 用途に応じてアルゴリズムを選ぶ(データ整合性→SHA-256/3、パスワード→Argon2id 等、MAC→HMAC)。
  • ソルトは長くランダムに、各エントリでユニークにする。
  • ハッシュに使う文字列はエンコーディングと正規化を固定する(例:UTF-8 + NFC)。
  • ハッシュや KDF のパラメータを設定ファイル等で外部化し、将来的にチューニング可能にする。
  • 古いアルゴリズムを使用しているユーザーを移行させる仕組みを用意する(ログイン時に再ハッシュなど)。
  • ライブラリは信頼できる実装を使用し、自前実装は避ける(危険)。

よくある誤解

  • 「ハッシュは暗号化と同じ」:異なります。暗号化は復号可能なことが前提ですが、ハッシュは一方向で不可逆です。
  • 「長いハッシュ = 安全」:出力長は重要ですが、アルゴリズム設計の脆弱性(衝突脆弱性、構造的攻撃)があれば長さだけでは救えません。
  • 「ソルトだけで十分」:ソルトはレインボーテーブルを無効化しますが、総当たり攻撃や高速ハッシュ実装に対しては KDF の導入が必要です。

まとめ(実務的な推奨)

一方向ハッシュ関数は広範なセキュリティ用途で不可欠なツールです。ただし安全に使うためには、用途に適したアルゴリズム選択、ソルトの導入、KDF の採用、実装の注意(正規化・定数時間比較)、およびアルゴリズムの陳腐化に対する運用方針が求められます。パスワード保存では Argon2id、一般的な整合性検査では SHA-256/512 や SHA-3、メッセージ認証では HMAC-SHA256 等が現時点での実務上の有力な選択肢です。

参考文献