【入門編】 暗号学的ハッシュのソルト生成における乱数生成器(CSPRNG)の重要性 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

こんにちは!日々の開発やインフラの保守、本当にお疲れ様です。新人のIT担当者や、これからセキュリティの勉強を始めるという方にとって、「暗号化」や「ハッシュ」といった言葉は、なんだか難しく壁が高く感じられてしまいますよね。

でも、安心してください。セキュリティの基本は、私たちが普段暮らしている現実世界の「防犯」の仕組みとまったく同じです。今回は、パスワードを守るための重要なパーツである「ソルト(Salt)」と、それを生成する「CSPRNG(暗号学的疑似乱数生成器)」について、身近な例えを交えながら一歩ずつ優しく紐解いていきましょう。

—

1. パスワードを守る「鍵」の仕組み:ハッシュとソルト

皆さんがWebサービスにログインするとき、パスワードを入力しますよね。システム側は、そのパスワードが正しいかどうかを確かめるために「ハッシュ関数(SHA-256やSHA-3など)」という仕組みを使っています。

ハッシュ関数とは、入力された文字列(パスワード)を、元の形が絶対に推測できないバラバラの文字列に変換するマジックボックスのようなものです。
例えば、「password123」というパスワードをハッシュ化すると、次のような不可逆な文字列に変換されます。

ef92b778bafe771e89245b89ecbc08a44a4e166c0665911681c319b90f49646f

もし悪意あるハッカーにデータベースが盗まれてしまっても、このハッシュ値(変換後の文字列)さえ保存しておけば、パスワードそのものが盗まれることはない……はずでした。しかし、ここでハッカーたちは「レインボーテーブル」というずる賢い攻撃方法を考え出しました。

レインボーテーブルという「合鍵の辞書」

ハッカーたちは、よく使われるパスワード(password123 や 123456 など)の組み合わせと、それに対応するハッシュ値をあらかじめ大量に計算し、辞書のようにまとめて持っています。これをレインボーテーブルと呼びます。

データベースからハッシュ値を見つけたハッカーは、自分の辞書をめくるだけで、「あ、このハッシュ値は password123 だ!」と一瞬で元のパスワードを見つけ出してしまうのです。これは、家や自転車の鍵として、世の中で一番出回っている「100円ショップの同じ型の鍵」をそのまま使っているようなものです。これでは泥棒に簡単に開けられてしまいますよね。

そこで登場するのが「ソルト(Salt)」です

この問題を解決するのが「ソルト(塩)」です。ソルトとは、パスワードをハッシュ化する直前にくっつける「ランダムな文字列(おまけ)」のことです。

現実世界に例えるなら、「合鍵を作る前に、鍵の表面にランダムな大きさの粘土を少しだけ貼り付けてから削る」ような作業です。たとえ同じ「password123」というパスワードを使っている人でも、くっつける粘土(ソルト)の形が人によって違えば、完成するハッシュ値も全く違うものになります。

これによって、ハッカーがあらかじめ用意した「辞書」が全く役に立たなくなるのです。

—

2. なぜ「予測可能なソルト」は命取りになるのか?

ここで非常に重要なポイントがあります。それは、「ソルトは必ず『予測不可能なランダムな値』でなければならない」ということです。

もし、システム側が適当な理由をつけて、以下のような「予測できるソルト」を使っていたらどうなるでしょうか?

  • ユーザーの登録順に連番(001, 002, 003…)をソルトにしている
  • ユーザーの登録日時(20231025 など)をそのままソルトにしている
  • プログラムの都合で、毎回同じ固定の文字列を使っている

これでは、泥棒が「あ、このサービスのソルトはきっと登録順の連番だな」と見抜いた瞬間、辞書をそのルールに合わせて作り直されてしまいます。ソルトとしての意味が完全に失われ、再びパスワードが裸同然になってしまうのです。

だからこそ、「誰も次に出る数字が絶対に予測できない、本物のランダムな値」を作る仕組みが必要になります。それが、今回主役となる CSPRNG(Cryptographically Secure Pseudo-Random Number Generator:暗号学的疑似乱数生成器) です。

—

3. OS提供の安全な乱数生成器(CSPRNG)を使う理由

私たちがプログラミングをする際、よく「ランダムな数字」を作るために言語標準の関数を使いますが、実はここに大きな罠があります。

例えば、プログラミング言語に最初から入っているような通常の乱数生成器(例:Pythonの random モジュールや、PHPの rand() など)は、プログラムの計算上の都合で作られている「疑似乱数」です。これらは、過去に出た数字のパターンを解析されると、「次にどの数字が出るか簡単に予測できてしまう」という弱点を持っています。これではセキュリティの現場では使い物になりません。

そこで登場するのが、OS(オペレーティングシステム)レベルで提供されている安全な乱数生成器(CSPRNG)です。

  • Linux / macOS の場合: /dev/urandom
  • Windows の場合: CryptGenRandom(モダンなPHPなどでは内部で自動的にOSの安全なAPIが呼び出されます)

これらは、パソコン内部のハードウェアのノイズ(マイクのわずかな拾い音や、キーボードを叩くタイミングの微妙なズレなど)をかき集めて作られており、世界中の天才ハッカーであっても次にどんな数字が出るか絶対に予測できません。

—

4. 実装コード例:安全なソルト生成とパスワード保存

それでは、実際に安全なパスワード保存の仕組みをPHPのコード例で見てみましょう。
現代の開発現場では、PHPの password_hash() という強力な関数を使うのがベストプラクティスです。この関数は、内部で自動的に安全なCSPRNGを使って強力なソルトを生成し、パスワードと混ぜ合わせてハッシュ化(ストレッチングも含む)してくれます。

私たちは複雑なアルゴリズムをゼロから書く必要はありません。正しい関数を正しく使うことが、最高峰のセキュリティへの第一歩です。

<?php
/**
 * 安全なパスワードハッシュ化と保存のサンプルコード
 * 
 * 脆弱な `md5()` や `sha1()`、自作のハッシュ関数は絶対に使用せず、
 * PHP標準の `password_hash()` を使用します。
 * この関数は内部で自動的にCSPRNGを使い、推測不可能なソルトを生成します。
 */

// 1. ユーザーがフォームから入力したパスワード(仮の値)
$plainPassword = 'User_Secure_Password_123!';

// 2. パスワードをハッシュ化する
// PASSWORD_DEFAULT を指定することで、PHPがその時点における最も安全なアルゴリズム(現在はBcryptなど)を選択します。
// ※内部で自動的に安全な乱数生成器(CSPRNG)が動き、一意のソルトが生成・付与されます。
$hashedPassword = password_hash($plainPassword, PASSWORD_DEFAULT);

// データベースには、この $hashedPassword(ソルトとハッシュ値が結合された文字列)を保存します。
// 例: $2y$10$e0MYzXyjpJS7Pd0RVvHwHe... (これがデータベースに格納される)
echo "データベースに保存するハッシュ値: " . htmlspecialchars($hashedPassword, ENT_QUOTES, 'UTF-8') . "<br>";


/**
 * 3. ログイン時のパスワード検証
 * ユーザーが入力したパスワードが合っているかを安全に確かめます。
 */
$loginInputPassword = 'User_Secure_Password_123!';

// password_verify() は、保存されたハッシュ値から自動的にソルトを取り出し、
// 入力されたパスワードと正しく一致するかを安全(タイミング攻撃に強い方法)に検証します。
if (password_verify($loginInputPassword, $hashedPassword)) {
    echo "ログイン成功!パスワードが一致しました。";
} else {
    echo "ログイン失敗。パスワードが間違っています。";
}
?>

コードのポイント

  • 自作しない: ソルトの生成やハッシュ化を自分で実装しようとせず、言語やフレームワークが提供する枯れた標準機能(PHPなら password_hash() など)を信頼して使いましょう。
  • CSPRNGの恩恵: 上記の関数を使うだけで、開発者が意識しなくてもOSレベルの強固なCSPRNGによるソルト生成の恩恵をフルに受けることができます。

—

まとめ

いかがでしたでしょうか? 今回は、パスワードの安全性を裏で支える「ソルト」と「CSPRNG(安全な乱数生成器)」について解説しました。

  • パスワードをそのまま保存してはいけない。
  • ハッシュ化する際には、レインボーテーブル対策として「ソルト」を混ぜる必要がある。
  • ソルトは予測不可能でなければならないため、通常の乱数ではなく、OSが提供する安全な乱数生成器(CSPRNG)の力に頼る必要がある。

セキュリティの世界は一見すると複雑で冷たく感じられますが、一つひとつの技術には「なぜこれが必要なのか」という人間味あふれる攻防の歴史があります。ぜひ今回の知識を日々の開発に活かして、安全で信頼されるシステムを一緒に作っていきましょう!

コメント

タイトルとURLをコピーしました