こんにちは!新人のIT担当者や、これからセキュリティの勉強を始めるという開発者の皆さん、日々の業務お疲れ様です。
システム開発をしていると、ユーザーの「パスワード」を安全に保存しなきゃいけない場面に必ず直面しますよね。「データベースにはそのまま保存しちゃダメで、ハッシュ化するんだよね?」というところまでは知っている方も多いと思います。
でも、「ただハッシュ化するだけでは、今のサイバー攻撃の前には紙同然の防御力になってしまう」ってご存知でしたか?
今回は、攻撃者の手口の裏をかき、私たちのシステムを守り抜くための必須技術「パスワードストレッチング」について、身近な防犯の例えを交えながら、一歩ずつ優しく紐解いていきたいと思います!
—
1. パスワードを「ただハッシュ化する」だけではなぜダメなのか?
まず、私たちが普段よく耳にする「ハッシュ化」のおさらいから始めましょう。
パスワードのハッシュ化とは、例えば「password123」のような元の文字列を、e6c...といった全く別の意味不明な文字列(固定長のデータ)に変換する仕組みです。
ここで重要なのは、ハッシュ化は「一方通行」の変換だということです。ハッシュ化された値から、元のパスワードを逆算することは理論上できません。だから安心……と、思いきや!
ここに、攻撃者たちが使う「ブルートフォース攻撃(総当たり攻撃)」や「レインボーテーブル攻撃(事前に計算したハッシュ値の辞書を使った攻撃)」という厄介な手口が存在します。
家の鍵に例えて考えてみましょう
想像してみてください。あなたは大切な財産を守るために、頑丈な玄関の扉に「ダイヤル式の鍵」をつけました。
- 普通のハッシュ化:ダイヤルの組み合わせを試すのに、1回「カチャッ」と回す時間が「0.000001秒」しかなかったとします。
もし泥棒が、超高速で回せるロボットアームを使ったらどうなるでしょう?
数百万通りの組み合わせなんて、ほんの数秒で全通り試されてしまい、あっさり鍵を開けられてしまいますよね。「ハッシュ化しているから大丈夫」と油断していると、現代の高性能なPCやGPUのパワーの前には、一瞬で突破されてしまうのです。
—
2. 攻撃者を疲れさせる魔法の技術「パスワードストレッチング」
そこで登場するのが、今回の主役である「パスワードストレッチング」です。
ストレッチング(Stretching)とは、英語で「伸ばす」という意味。何を引き延ばすかというとズバリ、「ハッシュ計算にかかる時間(計算コスト)」です。
鍵の例えの続き
先ほどのダイヤル式の鍵を改良してみましょう。今度は、ダイヤルを1回回すごとに、内部の歯車が複雑に噛み合って、「わざと1秒間、ガチャン、ウィーン……と重く回転する仕組み」を加えました。
- ストレッチングあり:1回試すのに「1秒」かかる。
これだとどうでしょう? 泥棒が全通り試すのに、何百万秒(何十日、何百年)もかかるようになり、「こんな面倒な家、狙うのをやめよう……」と諦めますよね。
これが、パスワードストレッチングの正体です。意図的に何万回もハッシュ計算を繰り返し(反復計算)、ログイン時の認証にはほんの少し(コンマ数秒)の遅延をあえて発生させることで、攻撃者が総当たりで破るためのコストを跳ね上げるのです。
—
3. 適切な反復回数の設定と「ソルト(Salt)」の重要性
「じゃあ、計算を何億回も繰り返して、1回ログインするのに10分かかるようにすれば最強じゃん!」と思ったそこのあなた、それはちょっと待った!
あまりに計算コストを高くしすぎると、今度は正当なユーザーがログインする時にも何秒も待たされることになり、サイトの使い勝手(UX)が最悪になってしまいます。また、サーバーのCPU負荷も跳ね上がり、最悪の場合はサービス運用妨害(DoS)状態になりかねません。
現代のWebアプリケーションにおいて、サーバーが認証処理にかけるべき「ちょうどいい時間」の目安は、およそ 「0.1秒〜0.5秒前後」 と言われています。
もう一つの相棒「ソルト(Salt)」
そして、ストレッチングとセットで絶対に覚えなければならないのが「ソルト(塩)」です。
ソルトとは、パスワードをハッシュ化する際、ユーザーごとにランダムに付加する無意味な文字列のことです。
世の中には「password」や「123456」といった、みんなが使い回す「よくあるパスワード」が存在します。攻撃者はこれらをあらかじめハッシュ化したリスト(レインボーテーブル)を作って待ち構えています。
しかし、パスワードにランダムな「ソルト」を混ぜてからハッシュ化・ストレッチングを行うと、例え同じパスワードを使っているユーザーが二人いても、データベースに保存される値は全く別のものになります。 これにより、辞書攻撃を完全に無力化できるのです。
—
4. 【実務編】PHPで学ぶ安全なパスワード保存の実装例
それでは、理論はこの辺にして、実際の開発現場でどう書くのかを見てみましょう!
PHPには、まさにこの「ソルトの生成」「ハッシュ化」「ストレッチング(コスト調整)」を自動で行ってくれる、最高に安全で簡単な標準関数 password_hash() が用意されています。
一歩ずつ、コードを見ていきましょう。
<?php
/**
* 新人エンジニアのための安全なパスワード登録・検証サンプル
*/
// 1. ユーザーが入力したパスワード(実際にはフォームから受け取ります)
$raw_password = 'MySuperSecretPassword123!';
/**
* 【パスワードのハッシュ化(登録時)】
* password_hash関数を使うことで、以下の処理が自動で行われます:
* - ランダムなソルトの自動生成
* - 適切なアルゴリズム(BcryptやArgon2)の適用
* - パスワードストレッチング(反復計算)の実行
*/
// BCRYPTアルゴリズムを使用する場合の例
$hashed_password = password_hash($raw_password, PASSWORD_BCRYPT, [
'cost' => 12 // コストパラメータ(ストレッチングの反復回数を決定。2の12乗回計算されます)
]);
// データベースには、この $hashed_password を保存します(元のパスワードは絶対に保存しない!)
echo "データベースに保存する値: " . htmlspecialchars($hashed_password, ENT_QUOTES, 'UTF-8') . "\n";
/**
* 【パスワードの検証(ログイン時)】
* ユーザーがログイン画面に入力したパスワードが、保存されているハッシュと一致するか検証します。
*/
$login_input_password = 'MySuperSecretPassword123!'; // ログイン時に入力されたパスワード
// password_verify関数は、内部でソルトを取り出し、同じストレッチングを行って比較します
if (password_verify($login_input_password, $hashed_password)) {
echo "ログイン成功!ようこそお越しくださいました。\n";
} else {
echo "ログイン失敗:パスワードが間違っています。\n";
}
?>
コードのポイント解説
PASSWORD_BCRYPTやPASSWORD_ARGON2ID:PHPが標準で提供する強力なハッシュアルゴリズムです。MD5やSHA-256をそのままパスワードに使うのは、ストレッチング機能がないため現在では御法度とされています。costパラメータ:これがストレッチングの回数を決めるキモです。マシンの性能や時代の進化に合わせて、この数値を調整(またはデフォルトにお任せ)することで、常に最適なセキュリティ強度を維持できます。
—
5. まとめ:セキュリティは「泥棒とのイタチごっこ」
いかがでしたでしょうか?
パスワードストレッチングは、「あえて計算に時間をかけさせることで、攻撃者のブルートフォース攻撃を非現実的なコストにする」という、非常にシンプルかつ強力な防衛策です。
現場のシステムを構築する際は、以下の鉄則をぜひ覚えておいてください。
1. パスワードは絶対に平文(そのまま)で保存しない。
2. 古いハッシュ関数(MD5やSHA-1など)は使わない。
3. password_hash() などの標準ライブラリを使い、ソルトとストレッチング(コスト調整)を必ず効かせる。
セキュリティの世界は、攻撃者と防御者の終わりのないイタチごっこです。でも、正しい知識と適切なツールを使えば、私たちのシステムは十分に堅牢な要塞にすることができます。
今日からあなたも、安全なパスワード管理の第一歩を踏み出してみませんか?
それでは、次回のセキュリティ解説もお楽しみに!
コメント