こんにちは!セキュリティの世界へようこそ。
新人のIT担当者の皆さんや、これからアプリ開発を始める一般開発者の皆さん、日々の業務お疲れ様です。「セキュリティ」って聞くと、なんだか難解な暗号や、近寄りがたい専門用語の壁があって少し身構えてしまいますよね。
でも、安心してください。セキュリティの根底にある考え方は、私たちが普段暮らしている現実世界の「防犯」とまったく同じなんです。
今回は、システム開発で避けて通れない「ハッシュ関数」と、ユーザーの大切なパスワードを守る仕組みについて、身近な例えを交えながら一歩ずつ優しく紐解いていきましょう!
—
1. 家の鍵で考えてみよう!「ハッシュ関数」ってなに?
いきなり「ハッシュ関数(SHA-2やSHA-3)」なんて言われると、頭がクラクラしてしまいますよね。まずは、身近な「合鍵を作る仕組み」に例えて考えてみましょう。
あなたが新しい家を買って、合鍵を作りたいとします。
鍵屋さんに元の鍵(データ)を渡すと、鍵屋さんはその形を正確に読み取り、数秒でピカピカの合鍵(ハッシュ値)を削り出してくれますよね?
この「元のデータを渡すと、一瞬で別の形に変換してくれる仕組み」が、まさにハッシュ関数です。ハッシュ関数には、次のようなすごく便利な特徴(お約束)があります。
- どんな長いデータでも、一定の短いサイズ(文字列)に変換してくれる
(例えば、1文字の「A」でも、分厚い小説1冊分でも、出力されるハッシュの長さは一定になります)
- 同じデータを入力すれば、100%絶対に同じ結果(ハッシュ値)が出てくる
- 入力データがほんの1文字でも変わると、全く似ても似つかない別のハッシュ値にガラッと変わる(雪崩効果と言います)
—
2. 攻撃者が狙う盲点!ハッシュ関数の「3大要件」
さて、このハッシュ関数、実はセキュリティの世界ではめちゃくちゃ重要な「金庫の鍵」として使われています。攻撃者(泥棒)たちは、この鍵を何とかして破ろうと日々あの手この手で狙っています。
セキュリティのプロたちは、ハッシュ関数が安全であるために、以下の「3つの壁(要件)」をクリアしているかを厳しくチェックしています。一緒に見ていきましょう!
① 原像計算困難性(げんぞうけいさんこんなんせい)
- 例え話: 泥棒が「合鍵」の形を見て、元の「本物の鍵」の形を完璧に復元しようとする作業です。
- 解説: ハッシュ関数は「一方通行のすべり台」のようなものです。上に登る(データをハッシュにする)のは一瞬ですが、すべり台の途中や下から「元の姿を完全に当てろ」と言われても、絶対に分からない仕組みになっています。これが「原像計算困難性」です。元のデータが分からないからこそ、安全なんですね。
② 第二原像計算困難性(だいにげんぞうけいさんこんなんせい)
- 例え話: あなたが持っている「本物の鍵A」とは違う形なのに、なぜか「同じ鍵穴が開いてしまう別の偽物の鍵B」を新しく見つけ出そうとする作業です。
- 解説: 攻撃者が「元のデータとは違うけれど、同じハッシュ値を生み出してしまう別のデータ」を偶然見つけ出すのは、砂漠の中から一粒のダイヤを探すよりも難しいと言われています。この性質があるおかげで、データの改ざんをすぐに検知できるのです。
③ 衝突耐性(しょうとつたいせい)
- 例え話: 「同じハッシュ値(同じ鍵穴に合う形)になる、任意の2つの異なる鍵のペア」を偶然見つけ出す作業です。
- 解説: データAとデータBの組み合わせが、たまたま同じハッシュ値になってしまう現象を「衝突(コリジョン)」と呼びます。優秀なハッシュ関数(SHA-2や最新のSHA-3など)は、この衝突が絶対に起きない(起きる確率が天文学的に低い)ように設計されています。
—
3. なぜデータベースに「パスワードの生データ」を保存しちゃいけないの?
ここで、よくある初心者の失敗談をお話しします。
「ユーザーが入力したパスワードなんだから、データベースにそのまま保存しておけばログイン画面で照合しやすいよね?」……これ、セキュリティ事故への特急券です。
もし、あなたが作ったWebサービスのデータベースが万が一サイバー攻撃にあってハッカーに盗まれたとします。そこにパスワードが「生データ(平文)」のまま保存されていたらどうなるでしょう? ユーザーの個人情報や、他のサービスで使い回しているパスワードまで一網打尽で漏洩してしまいます。ニュースでよく見る「リスト型攻撃」の餌食になってしまうわけです。
だからこそ、データベースにはパスワードの「生データ」ではなく、ハッシュ化した値を保存します。
ユーザーがログインするときは、入力されたパスワードをその場でハッシュ関数に通し、「データベースに保存されているハッシュ値と一致するかどうか」だけをチェックします。これなら、万が一データベースが盗まれても、パスワードの生データそのものは守られますよね!
—
4. 実務で使える!安全なパスワード保存のサンプルコード(PHP)
「なるほど、じゃあパスワードをハッシュ化すればいいんだね!」と思ったそこのあなた。実は、SHA-2やSHA-3をそのままパスワードのハッシュ化に使うのは、現代のセキュリティでは少し不十分なんです。
なぜなら、今のパソコンやグラフィックボード(GPU)は非常に性能が高く、SHA-2の計算なんて1秒間に何十億回も総当たり(ブルートフォース攻撃)できてしまうからです。
そこで実務では、パスワード専用の安全な仕組み(ソルトとストレッチング)が組み込まれた、PHPの password_hash() 関数のような標準機能を必ず使います。
実際のコードを見てみましょう!
<?php
/**
* 新人エンジニアのための安全なパスワード登録・検証サンプル
*
* PHPの password_hash() は、内部で自動的に「ソルト(ランダムな文字列の付加)」と
* 「ストレッチング(ハッシュ計算をあえて何万回も繰り返して重くする処理)」を行ってくれます。
*/
// 1. ユーザーが新規登録した(あるいは変更した)パスワードの生データ
$plainPassword = 'UserSecurePassword123!';
// 2. パスワードを安全にハッシュ化する(自動的に強力なソルトとストレッチングが適用されます)
// PASSWORD_DEFAULT を指定しておけば、PHPのバージョンアップに合わせて最適なアルゴリズム(BcryptやArgon2など)が選ばれます。
$hashedPassword = password_hash($plainPassword, PASSWORD_DEFAULT);
// データベースには、この $hashedPassword をそのまま保存します!
// (例: INSERT INTO users (password_hash) VALUES ('$hashedPassword');)
echo "データベースに保存されるハッシュ値: " . $hashedPassword . "\n";
// ==========================================
// 3. ユーザーがログインしようとした時の検証処理
// ==========================================
// ログイン画面でユーザーが入力したパスワード
$inputPassword = 'UserSecurePassword123!';
// password_verify() を使って、入力されたパスワードとDBのハッシュを安全に比較します
if (password_verify($inputPassword, $hashedPassword)) {
echo "認証成功!ログインを許可します。\n";
} else {
echo "認証失敗!パスワードが間違っています。\n";
}
?>
このコードの優れたポイント
- ソルト(Salt)の自動付加: パスワードの前にランダムな文字列をくっつけてからハッシュ化するため、同じ「password123」というパスワードであっても、ユーザーごとに全く異なるハッシュ値になります。これにより、攻撃者が「レインボーテーブル(あらかじめ計算されたハッシュの辞書)」を使って一撃で破ることが不可能になります。
- ストレッチング(Stretching): ハッシュ計算をわざと何万回も繰り返すことで、コンピュータに負荷をかけます。人間がログインする時は数ミリ秒の遅延なので気になりませんが、攻撃者が1秒間に何億回も総当たりしようとすると、マシンがパンクして攻撃を猛烈に遅く(事実上不可能に)することができます。
—
まとめ:一歩ずつ、確実なセキュリティ対策を
今回は、ハッシュ関数の基本特性(原像計算困難性、第二原像計算困難性、衝突耐性)と、パスワードを守るための現実的なアプローチについて解説しました。
最初は難しく感じたかもしれませんが、要するにこういうことです。
1. データはハッシュ関数ですべり台のように一方通行で変換する
2. パスワードの生データは絶対にデータベースに保存しない
3. パスワード専用の関数(password_hash など)を使って、ソルトとストレッチングの恩恵を受ける
日々の開発で「めんどくさいな」と思うセキュリティのルールには、すべて「泥棒からユーザーの大切な財産を守るための理由」があります。ぜひ今回の知識を実際のコードやインフラ設計に活かして、信頼されるエンジニアへの第一歩を踏み出してくださいね!
それでは、また次のセキュリティの教室でお会いしましょう!
コメント