みなさんこんにちは!Webアプリケーション開発やインフラの管理、本当にお疲れ様です。新人のIT担当者として奮闘している方や、セキュリティに初めて触れるという方にとって、覚えることが山のようにあって大変な時期ですよね。
「セキュリティ」と聞くと、なんだか難しそうな暗号や、ハッカーが映画のようにカチャカチャと黒い画面を叩いている姿を想像してしまうかもしれません。でも、基本の考え方は私たちの日常生活と全く同じなんです。
今回は、Webサイトの裏側でこっそり使われている「乱数(ランダムな数字)」のお話と、それが原因で起こってしまう恐ろしいセッションID予測攻撃について、身近な防犯にたとえながら一歩ずつ優しく紐解いていきたいと思います。一緒にしっかりと対策を学んでいきましょう!
—
1. 家の鍵の「合鍵」を作られる恐怖?セッションIDの役割とは
まずは、Webサイトで当たり前に使われている「セッション」の仕組みからお話ししますね。
皆さんがネットショッピングや社内ポータルサイトにログインするとき、IDとパスワードを入力しますよね。ログインが成功すると、サーバーはあなたに対して「あなたは無事にログインした人ですよ」という証明書を渡します。これがセッションIDです。
身近な例えで考えてみましょう。
- IDとパスワード = 家の「本物の鍵」
- セッションID = 玄関を開けて家に入ったあと、リビングで自由に過ごすための「通行証(あるいはルームキー)」
毎回ページを移動するたびに「私は〇〇です、パスワードはこれです」と証明するのは大変ですし、何より通信の途中でパスワードを盗み見られたら大惨事になります。だから、ログインした最初の1回だけパスワードを使い、そのあとは「セッションID」という身分証をブラウザ(ChromeやSafariなど)に持たせてやり取りをするわけです。
もし、このセッションIDが他人に知られてしまったらどうなるでしょうか?
泥棒があなたの家の合い鍵を手に入れたのと同じで、攻撃者はあなたになりすまして、勝手に買い物をしたり、機密情報を盗み見たりできてしまいます。だからこそ、このセッションIDは絶対に他人に推測されてはいけないものなのです。
—
2. なぜ予測されてしまうのか?「いかさまガチャ」の罠
では、どうしてその大切なセッションIDが攻撃者に予測されてしまうのでしょうか?
ここに「不適切な乱数生成(暗号学的に安全でない擬似乱数生成器の使用)」というセキュリティの大きな落とし穴があります。
システムがセッションIDなどの重要なランダムな文字列を作るとき、「乱数生成器」というプログラムを使います。ここで言う「乱数」とは、人間には予測できないバラバラの数字のことです。
ここで少し、ゲームセンターの「ガチャ」を思い浮かべてみてください。
- 安全なガチャ(CSPRNG):
完全にランダムなボールが入っていて、次に何が出るか絶対に分からない、物理的にも公平なガチャです。
- 不安全なガチャ(予測可能なPRNG):
実はこのガチャ、裏で「1番が出たら次は絶対に出た回数+1の数字が出る」という単純なルール(計算式)で動いています。
古いプログラミング言語の機能や、標準的な数学用の乱数生成器(PHPの rand() や JavaScriptの Math.random() など)は、まさに後者の「ルールが読めてしまうガチャ」なんです。
攻撃者はどうやってこれを悪用するでしょうか?
彼らは、あなたのサイトが発行したセッションIDをいくつかこっそりと観察します。「おや、さっきのセッションIDは 1005 で、次のやつが 1006 になったぞ?」と気づけば、その裏にあるルール(計算式)を完全に暴いてしまいます。
ルールが分かってしまえば、未来のセッションID(次に誰かがログインしたときに発行されるID)が手に取るように予測できてしまいます。これがセッションID予測攻撃のメカニズムです。
—
3. 実際の脆弱なコードと、安全なコードの書き方
それでは、開発の現場で私たちがどのように間違えてしまい、どう直すべきなのかを見ていきましょう。今回は多くのWebサイトで使われているPHPを例に説明しますね。
【危険な例】「数学的な計算」で乱数を作っているコード
以下のコードは、一見するとランダムな文字列を作っているように見えますが、実は大きなセキュリティホールがあります。
<?php
// 【危険な例】古い関数や通常の数学的乱数を使っているケース
function generateBadSessionId() {
// rand() や mt_rand() は「暗号の安全性を考慮していない」ため、
// 過去の値から次の値を予測される危険性があります!
$randomValue = mt_rand();
// ハッシュ化してそれっぽく見せかけても、大元の数字が予測可能なら意味がありません
return md5($randomValue . time());
}
?>
このようなコードを使っていると、攻撃者にセッションIDの規則性をみすかされ、ユーザーのなりすましを許してしまいます。
【安全な例】「暗号学的に安全な擬似乱数生成器(CSPRNG)」を使うコード
では、どうすれば安全になるのでしょうか?
現代のプログラミング言語には、暗号の用途に特化した、予測不可能な「安全なガチャ(CSPRNG)」が標準で用意されています。PHPであれば random_bytes() や random_int() を使います。
<?php
// 【安全な例】CSPRNG(暗号学的に安全な擬似乱数生成器)を使用するケース
function generateSecureSessionId() {
// 32バイト(256ビット)の十分な長さを持つ、予測不可能なランダムなデータを生成
// random_bytes() はOSの暗号学的機能を利用しているため安全です
$bytes = random_bytes(32);
// 16進数の文字列に変換してセッションIDとして返却
return bin2hex($bytes);
}
?>
このように、「暗号学的に安全な関数」を選ぶだけで、セッションIDは一気に鉄壁の守りになります。これなら泥棒も規則性を見つけ出すことができませんよね。
—
4. 実務で役立つ!セッション管理を強固にするための設定
コードを安全なものに直したら、次はインフラやフレームワーク側の設定を見直しましょう。実務の現場で必ずチェックしてほしいポイントをまとめました。
1. フレームワークの標準機能に頼る
LaravelやDjango、Ruby on Railsなどのモダンなフレームワークは、標準で安全なCSPRNGを使ってセッションIDを生成してくれます。変に自分でセッション生成のロジックを書かず、フレームワークの標準機能をそのまま信じて使いましょう。
2. クッキーの属性(HttpOnlyとSecure)を必ず有効にする
発行されたセッションIDをブラウザに保存する際、Cookie(クッキー)を使います。この設定が甘いと、クロスサイトスクリプティング(XSS)などの別の脆弱性からセッションIDが盗まれてしまいます。
HttpOnly属性:JavaScriptからCookieにアクセスできないようにする(盗難防止)Secure属性:暗号化された通信(HTTPS)でのみCookieを送信する(盗聴防止)
以下は、PHPでセッションクッキーの安全な設定を行う例です。
<?php
// セッションを開始する前に、安全なクッキーパラメータを設定します
session_set_cookie_params([
'lifetime' => 0, // ブラウザを閉じたらセッションを破棄
'path' => '/', // サイト全体の共通パス
'domain' => 'example.com', // 自社のドメインを指定
'secure' => true, // HTTPS通信でのみ送信を許可 (Secure属性)
'httponly' => true, // JavaScriptからのアクセスを禁止 (HttpOnly属性)
'samesite' => 'Lax' // CSRF対策としてLaxまたはStrictを指定
]);
// 安全な設定を行った後にセッションを開始
session_start();
?>
—
まとめ:一歩ずつ、確実に安全なシステムを作っていこう
今回は、セッションID予測攻撃のメカニズムと、安全な乱数生成(CSPRNG)の重要性について解説しました。
- セッションIDはWebサイトにおける「通行証(合い鍵)」である。
- 古い乱数生成器(
rand()やmt_rand()など)はルールが読まれてしまうため危険。 - 暗号学的に安全な関数(PHPなら
random_bytes()など)やフレームワークの機能を利用する。 - Cookieの設定(
HttpOnlyやSecure)を必ず有効にして、発行後もしっかり守る。
最初は覚えることが多くて圧倒されてしまうかもしれませんが、一つひとつの仕組みを「なぜ必要なのか」という視点で見つめ直せば、決して難しいものではありません。
日々の開発の中で「このデータはランダムであるべきか?」「予測されたらやばくないか?」と少し立ち止まって考える習慣をつけるだけで、あなたの作るWebアプリケーションのセキュリティはぐっと強固になります。
焦らず、一歩ずつ確実に、安全なシステム作りを楽しんでいきましょう!応援しています!
コメント