【入門編】 認証情報の平文保存とハッシュ化アルゴリズム(Argon2/bcrypt)の選定 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

こんにちは!アプリケーションの開発やインフラの管理、毎日本当にお疲れ様です。

システムを作るとき、「ユーザーのパスワードをどうやって安全に保存するか」という問題に必ず直面しますよね。新米のIT担当者や開発者の方から、「ネットで調べるとMD5やSHA-256、果てはbcryptやArgon2なんて言葉が出てきて、どれを使えばいいか分からない!」という悲鳴をよく耳にします。

今回は、攻撃者がデータベースを盗み見たときに何を考えているのかを覗き見つつ、私たち開発者がなぜ最新のハッシュ化アルゴリズム(Argon2やbcrypt)を選ばなければならないのか、身近な防犯の例えを交えながら優しく紐解いていきたいと思います。一歩ずつ、一緒に学んでいきましょう!

—

1. 家の鍵で例える「パスワード保存」の基本

突然ですが、あなたが大切なお金や書類を保管するために「家」を建てたとします。その家の玄関の鍵(パスワード)を、どうやって管理するのが一番安全でしょうか?

やってはいけない最悪のパターン:平文保存

一番やってはいけないのが、合鍵の形をそのまま紙に書いて、誰でも見える玄関の壁に貼り付けておくような状態です。これをセキュリティの世界では「平文(ひらぶん)保存」と呼びます。

もしデータベース(家の中)に、ユーザーのパスワードが P@ssw0rd123 のようにそのままの文字で保存されていたらどうなるでしょうか?
万が一、悪意あるハッカー(泥棒)にデータベースのデータをごっそり盗まれてしまったら、一瞬で全てのユーザーの鍵が奪われてしまいます。「別のサイトでも同じパスワードを使い回しているよ!」というユーザーがいれば、その人のメールアドレスやSNS、銀行口座まで芋づる式に突破されてしまう大惨事になります。だからこそ、パスワードをそのまま保存するのは絶対にNGなんです。

鍵の代わりに「指紋」や「金庫」を使う(ハッシュ化)

では、どうすればいいのでしょうか?
パスワードはそのまま保存せず、「一方向ハッシュ関数」という特殊な数学のミキサーにかけます。これは、元のパスワードを投入すると、グチャグチャに撹拌(かくはん)されて全く別の文字列(ハッシュ値)に変換してくれる仕組みです。

  • P@ssw0rd123 ➔ ミキサーにかける ➔ $2y$10$e0MYzXyjp... (ハッシュ値)

ここで重要なのは、「ミキサーにかけた後のハッシュ値から、元のパスワード(P@ssw0rd123)を逆算することは理論上できない」という点です。ログインするときは、ユーザーが入力したパスワードを同じミキサーにかけて、データベースにあるハッシュ値と「一致するかどうか」だけを比べます。これなら、万が一データベースが盗まれても、元のパスワードが直接バレるリスクを防ぐことができますよね。

—

2. 泥棒の新しい手口:「レインボーテーブル」と「GPUの暴力」

「なるほど、ハッシュ化しておけば安心ですね!」と思ったそこのあなた、実はセキュリティの世界はそんなに甘くありません。泥棒たちも進化しています。

ハッシュ化された文字列を見ただけでは元に戻せないのですが、泥棒たちは「よく使われそうなパスワードを片っ端からミキサーにかけて、結果のリストをあらかじめ作っておく」というズルい手を使います。これをレインボーテーブルと呼びます。
「このハッシュ値と一致するってことは、元のパスワードは 123456 だな!」と、辞書をめくるように一瞬で暴かれてしまうのです。

さらに現代の攻撃者は、ゲームなどで使われる超高性能なグラフィックボード(GPU)を何枚も並べ、「1秒間に何十億回もパスワードのミキサーがけを試す」という力技(ブルートフォース攻撃)を使ってきます。古いハッシュ化アルゴリズム(MD5やSHA-256など)は、「計算があまりにも速すぎる」がゆえに、泥棒のこの力技の前には数秒で崩壊してしまうのです。

—

3. 防犯をさらに強固にする「ソルト(Salt)」と「コスト(Cost)」

ここで登場するのが、今回の主役である「ソルト(塩)」と、「計算コスト(Argon2 / bcrypt)」という概念です。

「ソルト」で辞書攻撃を防ぐ

ソルトとは、パスワードをハッシュ化するミキサーに混ぜる「ランダムな適当な文字列」のことです。
例えば、ユーザーAのパスワードが同じ P@ssw0rd123 であっても、システム側で自動生成したランダムな文字列(ソルト:a8f2k9など)をくっつけてからミキサーにかけます。

  • ユーザーAの保存データ:P@ssw0rd123 + a8f2k9 ➔ ハッシュ値A
  • ユーザーBの保存データ:P@ssw0rd123 + z7x1m4 ➔ ハッシュ値B

こうすることで、たとえ同じパスワードを使っていても、データベースに保存されるハッシュ値は全く別のものになります。泥棒は「全員分の共通の辞書(レインボーテーブル)」が使えなくなり、ユーザー1人ひとりのために膨大な計算をやり直さなければならなくなります。これは防犯において非常に強力な壁になります。

「計算コスト」で泥棒の足を遅くする

そして、泥棒の力技(GPUによる超高速な総当たり攻撃)に対抗するための切り札が、bcryptやArgon2といった最新のハッシュ化アルゴリズムです。

これらは、あえて「ハッシュ化の計算にわざと時間がかかる(CPUやメモリのパワーを多く消費する)ように設計されている」という変態的な特徴を持っています。
私たち通常のユーザーがログインボタンを押したとき、サーバー側で認証のために0.1秒〜0.5秒ほど待たされるのは人間にとっては一瞬ですし、何の問題もありませんよね。しかし、1秒間に何十億回も試したい泥棒側からすると、1回計算するのに時間がかかるアルゴリズムを使われると、攻撃のスピードが何万分の一にまでスローダウンさせられます。「やってられない!」と泥棒に諦めさせるための、最高の足止めトラップというわけです。

  • bcrypt(ビークリプト): 長年信頼されてきた実績のあるアルゴリズム。コストパラメータ(処理の重さ)を自由に変更できます。
  • Argon2(アルゴンツー): パスワードハッシュコンペティションで優勝した現在の最先端アルゴリズム。CPUだけでなくメモリも大量消費させることで、専用のハードウェアを使った最新の攻撃すらも徹底的に阻止します。

—

4. 実装例:PHPで安全なパスワードハッシュ(bcrypt / Argon2)を使ってみよう

それでは、実際の開発現場でどのように実装すればよいのでしょうか?
現代の主要なプログラミング言語には、安全なハッシュ化関数が標準で用意されています。今回はPHPを例に取って、安全な実装方法を見ていきましょう。

パスワードを登録(ハッシュ化)するコード

新しいユーザーが登録されたり、パスワードを変更したりするときは、password_hash() 関数を使います。これだけで、内部で自動的に強力なソルトの生成とハッシュ化を行ってくれます。

<?php
// ユーザーがフォームに入力した平文のパスワード(実際にはバリデーションやサニタイジングが必須です)
$plainPassword = 'UserSecurePassword123!';

// password_hash()関数を使って安全にハッシュ化する
// 第2引数には、使用するアルゴリズムを指定します。
// PASSWORD_DEFAULT を指定しておけば、PHPのバージョンアップに合わせて最適な最新アルゴリズム(現在は主にbcrypt)が自動選定されます。
// ※より高度なセキュリティが求められる場合は PASSWORD_ARGON2ID を指定することも可能です。
$hashedPassword = password_hash($plainPassword, PASSWORD_DEFAULT);

/* 
 * データベースには、$hashedPassword(例: $2y$10$... で始まる文字列)をそのまま保存します。
 * この中には、アルゴリズムの種類、設定したコスト、自動生成されたソルト、ハッシュ値が
 * すべて結合されて含まれているため、別途ソルトをデータベースの別カラムに保存する必要はありません!
 */
echo "データベースに保存する値: " . htmlspecialchars($hashedPassword, ENT_QUOTES, 'UTF-8');
?>

ログイン時にパスワードを検証するコード

ユーザーがログインしようとしたときは、入力された平文のパスワードと、データベースに保存されているハッシュ値を password_verify() 関数で比較します。

<?php
// ユーザーがログインフォームに入力したパスワード
$inputPassword = 'UserSecurePassword123!';

// データベースから取得したハッシュ化済みパスワード(実際にはDBからSELECTしたもの)
// ここでは例として、先ほど生成されたハッシュがあると仮定します
$storedHashFromDb = '$2y$10$EXAMPLE_HASH_STRING_ABCDE...';

// password_verify()関数で検証を行う
// この関数は、保存されたハッシュからソルトやコストを自動で読み取り、
// 入力されたパスワードを同じ条件でハッシュ化して安全に比較してくれます。
if (password_verify($inputPassword, $storedHashFromDb)) {
    // 認証成功!セッションを発行してマイページへ誘導します
    echo "ログイン成功!ようこそ!";
} else {
    // 認証失敗
    echo "パスワードまたはユーザー名が間違っています。";
}
?>

どうでしょうか?フレームワークや標準関数を正しく使うだけで、複雑なソルトの管理やアルゴリズムの選定を安全に行うことができるんです。

—

5. まとめ:今日からできる一歩

今回は、パスワードの平文保存の危険性と、ソルト、そしてbcryptやArgon2といった最新のハッシュ化アルゴリズムの重要性を、防犯の例えを交えて解説しました。

  • パスワードは絶対に平文で保存しない。
  • 古いMD5やSHA-256ではなく、計算コストをかけられる bcrypt や Argon2 を選択する。
  • 自分で変な暗号化ロジックを自作せず、言語やフレームワークが提供する標準の安全な関数(PHPなら password_hash() など)を素直に利用する。

セキュリティの対策と聞くと難しく身構えてしまうかもしれませんが、正しい知識を持って「安全な道具」を選ぶことさえできれば、泥棒たちの侵入をがっちり食い止めることができます。
一歩ずつ、セキュアなシステム作りを楽しみながら進めていきましょう!あなたの開発ライフを応援しています!

コメント

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