ハッシュ関数とは ― 改ざん検出とパスワードの保存
ハッシュ関数は、
ハッシュ関数とは、入力を固定長の値に変える一方向の技術
ハッシュ関数は、どんな長さの入力からも決まった長さの値を作る技術です。作った値から元の入力を求めることは、現実的にはできません。ダウンロードページに載っている「SHA-256」の値は、この性質を使っています。Webサービスがパスワードをそのまま保存しない仕組みも同じです。
作った値をハッシュ値、またはメッセージダイジェストと呼びます。暗号化は、鍵を使って元に戻せる形にデータを変換する技術です。ハッシュ関数は、鍵を使わず、元に戻せない形で値を作ります。この性質を生かして、データが途中で変えられていないかの確認や、パスワードの保存に使います。
性質:一方向性・固定長・衝突困難性
FIPS 180-4は、アメリカの国立標準技術研究所(NIST)の規格です。SHA-256などの方式を「反復的な一方向のハッシュ関数」と説明しています。一方向性とは、ハッシュ値から元の入力を、現実的な時間では計算で求められないという性質です。
出力の長さは、入力の長さに関係なく一定です。この性質を固定長と呼びます。たとえばSHA-256は、1文字の入力でも長い文章の入力でも、常に256ビットの値を作ります。
もう1つの性質が衝突困難性です。違う2つの入力から、同じハッシュ値ができることを衝突と呼びます。FIPS 180-4は、意図的に衝突する2つの入力を見つけることも、計算上不可能だとしています。
FIPS 180-4は、さらにもう1つの性質を挙げています。入力をどこか1か所でも変えると、高い確率でハッシュ値が変わるという性質です。この性質は、後の節の図で確かめられます。
代表的な方式:SHA-256とMD5・SHA-1が推奨されない理由
代表的な方式はSHA-256です。SHA-2というファミリーの一種で、NISTのFIPS 180-4で定められています。CRYPTRECは、日本の暗号評価の取り組みです。SHA-256は、その「電子政府における調達のために参照すべき暗号のリスト」でも、現行暗号リストに載っています。
| 方式 | 出力の | CRYPTRECの |
|---|---|---|
| SHA-256(SHA-2) | 256ビット | 現行暗号リスト |
| SHA-384/SHA-512(SHA-2) | 384/512ビット | 現行暗号リスト |
| SHA-1 | 160ビット | 運用監視暗号リスト(互換性維持のためだけに利用を認める) |
| MD5 | 128ビット | 現行・運用監視のどちらのリストにも載っていない |
SHA-1とMD5は、新しく作るシステムでは使いません。2017年、GoogleとCWI Instituteの研究チームが発表を行いました。同じSHA-1のハッシュ値を持つ異なる2つのファイルを作ったという内容です。NISTはこの発表を受けて、より強いハッシュ関数への移行の必要性を強調しています。
NISTは、SHA-1を2011年から非推奨とし、2013年末にはデジタル署名での使用を禁止しました。CRYPTRECの分類でも、SHA-1は互換性維持のためだけに利用を認める運用監視暗号リストに入っています。
MD5は、1992年のRFC 1321で定められた、128ビットの値を作る古い方式です。2011年のRFC 6151は、MD5への攻撃が公表されたことを問題にしています。デジタル署名のように衝突困難性が必要な用途では、MD5を使えないとしています。MD5は、CRYPTRECの現行暗号リスト・運用監視暗号リストのどちらにも載っていません。
図で確かめる:1文字変えるとハッシュ値はどう変わるか
「Passw0rd1」と、最後の1文字だけを変えた「Passw0rd2」で、SHA-256のハッシュ値を比べます。macOSやLinuxでは、printf '%s' 'Passw0rd1' | shasum -a 256 で同じ値を確かめられます。末尾に改行を付けて計算すると、値が変わります。
- 入力文字列
- Passw0rd1
- アルゴリズム
- SHA-256
- ハッシュ値(16進数64桁)
- 963ef1140e817de9c8597680a08c4a70aea11b67cf74a4716a1b05ad9a00d11a
- 入力文字列
- Passw0rd2
- アルゴリズム
- SHA-256
- ハッシュ値(16進数64桁)
- 64036712376af58109c972db893bf7e87b3a5a06a4bcd5aa1e5d1e069b43a9dc
2つのハッシュ値を2進数にして比べると、256ビットのうち121ビットが異なります。1文字変えただけでも、ハッシュ値の半分近いビットが入れ替わります。
改ざん検出への応用
1文字変えるだけでハッシュ値が大きく変わる性質は、データが途中で変えられていないかの確認に使えます。送信者は、本文のハッシュ値を計算して一緒に送ります。受信者は、受け取った本文からハッシュ値を計算し直して比べます。
2つの値が一致すれば、本文は途中で変わっていないと判断できます。一致しなければ、途中で変わったことが分かります。ただし、本文とハッシュ値の両方を書き換えられると、この比較では気付けません。そのため、ハッシュ値は本文とは別の経路で受け取るか、デジタル署名で守ります。秘密の鍵を混ぜて、書き換えた人が値を作り直せないようにする方法がメッセージ認証符号です。
暗号の基礎の記事で説明したデジタル署名でも、ハッシュ値を使います。RSAやECDSAなどの方式では、本文そのものではなく、このハッシュ値をもとに秘密鍵で署名を作ります。署名を検証する側も、受け取った本文からハッシュ値を計算し直してから、署名を確かめます。
電子証明書とPKIの記事で説明した証明書の署名も、同じ仕組みです。認証局は、証明書の中身から計算したハッシュ値をもとに署名します。
証拠のディスクを複製したときも、原本と複製のハッシュ値を比べて同一性を確かめます。この使い方はデジタルフォレンジックスの記事で説明しています。記録のまとまりごとに前のハッシュ値を持たせ、書き換えを見つける仕組みがブロックチェーンです。
パスワード保存とハッシュ化
Webサービスは、利用者のパスワードをそのまま保存しません。保存するのは、パスワードのハッシュ値です。ログイン時には、入力されたパスワードのハッシュ値を計算して、保存された値と比べます。一方向性があるため、保存されたハッシュ値だけを見ても、元のパスワードは分かりません。データベースが漏えいしても、攻撃者はハッシュ値から直接パスワードを読み取れません。
ただし、ハッシュ値が漏えいすると、パスワードの候補を次々に試す攻撃ができます。候補のハッシュ値を計算し、保存された値と一致するかを確かめる方法です。候補を単語の一覧から選ぶものが辞書攻撃です。すべての組み合わせを試すものが総当たり攻撃です(不正アクセスの記事)。解読されたパスワードは、別のサービスを狙うパスワードリスト攻撃にも使われます。
ソルト:同じパスワードでも違う値にする
ソルトを使わないと、同じパスワードを使う利用者は、保存されるハッシュ値も全員同じになります。攻撃者は、よく使われるパスワードのハッシュ値をあらかじめ計算した対応表を用意できます。表に載っているパスワードなら、比べるだけで、同じ値を持つ利用者全員のパスワードを割り出せます。
ソルトは、ハッシュ化する前のパスワードに付け加える、利用者ごとに違う値です。NISTのSP 800-63B(2025年の第4版)は、パスワードにソルトを付けてハッシュ化することを求めています。ソルトの長さは、32ビット以上にするとしています。
同じパスワードでも、ソルトが違えば保存されるハッシュ値も違う値になります。次の図で、ソルトがある場合と無い場合を比べます。
図で確かめる:ソルトの有無を比べる
次の図は、同じパスワード「Passw0rd1」を使う利用者A・Bの例です。ソルトなしとソルトありを切り替えると、保存されるハッシュ値の違いを比べられます。仕組みを見やすくするため、この例はパスワードの後ろにソルトを付けて、SHA-256を1回かけただけの値です。ソルトも短くしています。
- 利用者Aのパスワード
- Passw0rd1
- 保存されるハッシュ値
- 963ef1140e817de9c8597680a08c4a70aea11b67cf74a4716a1b05ad9a00d11a
- 利用者Bのパスワード
- Passw0rd1
- 保存されるハッシュ値
- 963ef1140e817de9c8597680a08c4a70aea11b67cf74a4716a1b05ad9a00d11a
2人とも同じパスワードを使っているので、保存されるハッシュ値も同じになります。攻撃者は、よく使われるパスワードのハッシュ値を計算した対応表(レインボーテーブルなど)と比べるだけで、同じ値を持つ利用者全員のパスワードを割り出せます。
- 利用者Aのパスワード
- Passw0rd1
- 利用者Aのソルト
- a1b2c3
- 保存されるハッシュ値
- c94127bb36042de66d01cbd629a6dd0af4c8e8e8f94e3b3585f22989473455c4
- 利用者Bのパスワード
- Passw0rd1
- 利用者Bのソルト
- x9y8z7
- 保存されるハッシュ値
- b94a1f8225febeda3f73b09c54fa6a0755e1e999c65d929d68352b0058fc1f55
同じパスワードでも、利用者ごとに別のソルトをハッシュ化の前に付けるので、保存されるハッシュ値は全員違う値になります。事前に計算した対応表は、ソルトごとに作り直さないと使えません。
実際のパスワード保存では、SHA-256を1回かけるだけでは足りません。SP 800-63Bは、パスワード用のハッシュ方式を使うよう求めています。この方式は、パスワードとソルトに加えて、計算の手間を決める値(コスト)を入力にとります。NISTが認める方式の例は、SP 800-132で定めるPBKDF2です。これは、パスワードから鍵を作る鍵導出関数の1つです。
PBKDF2は、ハッシュ化の処理を決めた回数だけ繰り返して、1回の計算に時間をかけます。この工夫をストレッチングと呼びます。SP 800-63Bは、コストを性能が許す範囲でできるだけ高くするよう求めています。計算機の性能向上に合わせて、引き上げることも求めています。1回の計算が重いほど、攻撃者が候補を試す速度は遅くなります。
練習問題(全5問)
正解:ハッシュ値から、
一方
正解:256ビット
NISTの
正解:256ビットの
1文字だけ
正解:実際に
2017年、
正解:ソルトを、
ソルトは、
選んでから「採点する」を押すと、解説つきで答え合わせができます。
参考にした資料
- NIST FIPS 180-4(Secure Hash Standard。性質・出力の長さ)
- NIST SP 800-63B-4(3.1.1.2節。パスワード保存のソルトとパスワード用のハッシュ方式)
- RFC 1321(MD5の出力長)
- RFC 6151(MD5を衝突困難性が必要な用途に使えない理由)
- NIST CSRC「Research Results on SHA-1 Collisions」(2017年2月のお知らせ)
- CRYPTREC暗号リスト(令和5年3月30日版。ハッシュ関数の分類)
関連記事
おすすめの本 PR
セキュリティとデータベースは、基本情報技術者試験の対策本でまとめて学べます。

