【入門編】 セッション固定化攻撃(Session Fixation)の認証フロー検証 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

こんにちは!Webサービスの開発やセキュリティ対策に携わるようになると、色々な専門用語が出てきて頭が痛くなりますよね。「セッション固定化攻撃」なんて言われると、なんだか難しそうだな……と身構えてしまうかもしれませんが、大丈夫です。一歩ずつ、身近な例えから紐解いていけば、誰でもしっかり理解して対策できるようになりますよ。

今回は、この「セッション固定化攻撃」の仕組みと、開発現場で私たちがどうやってそれを防げばいいのかを、分かりやすく解説していきますね。

—

1. セッション固定化攻撃って、どんな手口?(家の鍵の例え)

まずは、Webサイトの「ログイン」の仕組みと、セッションがどう使われているかを考えてみましょう。

Webの世界では、一度ログインしたあともページを移動するたびに「私はログイン中の〇〇です」と証明し続ける必要があります。その証明書代わりになるのが「セッションID」と呼ばれるものです。通常は、ブラウザの「Cookie(クッキー)」という場所に保存されています。

これを「家の鍵」に例えてみましょう。

1. 通常の正しい流れ:あなたがお店に行って、新しく会員登録(またはログイン)をします。すると店員さんが、「新しく作りたての、あなた専用の鍵(新しいセッションID)」を渡してくれます。あなたはその鍵を使って、自分のお部屋に出入りしますよね。
2. セッション固定化攻撃の流れ:悪巧みをしている泥棒がいます。泥棒は、まずそのお店の合鍵(攻撃者があらかじめ用意したセッションID)をこっそり手に入れます。そして、被害者であるあなたに「この鍵を使ってお店に入ってよ!」と、その合鍵を強制的に使わせます(URLのパラメータなどにそのIDを混ぜ込んで踏ませるなど)。
3. 何が起きる?:あなたがその合鍵を使ってログインを完了したとします。本来なら「新しい鍵」に替わらなければいけないのですが、脆弱性のあるシステムだと、あなたが使っていた「泥棒が用意した合鍵」のまま、ログイン状態になってしまうのです!
4. 結末:ログイン後も同じ鍵を使っているので、泥棒もあなたと同じ鍵(同じセッションID)であなたのお部屋(マイページなど)に自由に出入りできるようになってしまいます。これがセッション固定化攻撃の正体です。

つまり、「ログインしたのに、鍵の番号が新しくならない(使い回されている)」という点をついて、攻撃者にセッションを乗っ取られてしまうわけですね。

—

2. なぜこの脆弱性が生まれてしまうのか?

開発の現場で、なぜこの脆弱性が作り込まれてしまうのでしょうか。原因はとてもシンプルで、「ログインの前後でセッションIDを作り直していない(再生成していない)」からです。

多くの初心者の開発者は、「ログイン成功!よし、セッションに user_id を保存しよう!」という処理だけに意識が向いてしまいます。

// 【危険なコードの例(PHP)】ログイン処理
session_start();

// ユーザーからの入力を検証して認証成功とする
if ($username === 'yamada' && $password === 'secret123') {
    // 認証されたユーザーIDを現在のセッションに保存するだけ
    // ※セッションIDはログイン前のものがそのまま引き継がれています!
    $_SESSION['user_id'] = 123;
    
    header('Location: /mypage.php');
    exit;
}

上記のコードの何が問題か分かりますか?
$_SESSION['user_id'] = 123; と値を入れていますが、セッションID(ブラウザのCookieに入っている PHPSESSID など)そのものは、ログインする前と全く同じものが使われ続けています。ここに攻撃の隙が生まれてしまうのです。

—

3. 実践!セッション固定化を防ぐための鉄則コード

では、この脆弱性を防ぐためにはどうすればよいのでしょうか?
答えは簡単です。「ログインが成功した瞬間、古いセッションを捨てて、まったく新しいセッションID(新しい鍵)を強制的に発行する」これだけです。

PHPを例に、安全な実装方法を見てみましょう。

<?php
// 安全なログイン処理の例(PHP)
session_start();

// 認証処理
if ($username === 'yamada' && $password === 'secret123') {
    
    // 【重要】セッション固定化攻撃を防ぐため、ログイン成功時にセッションIDを完全に新しくする
    // 引数に true を指定することで、古いセッションファイルもサーバー側から削除します
    session_regenerate_id(true);
    
    // 新しくなったセッションにユーザー情報を保存する
    $_SESSION['user_id'] = 123;
    $_SESSION['login_time'] = time();
    
    header('Location: /mypage.php');
    exit;
}

たったこれだけの一行、session_regenerate_id(true); をログイン成功直後に入れるだけで、攻撃者が用意した「合鍵」は無効化され、被害者には完全に新しいセッションIDが発行されます。泥棒は締め出されるというわけですね。

—

4. 開発やインフラで見落としがちなセキュリティヘッダー

セッションを守るための対策は、プログラムのコードだけではありません。ブラウザとサーバーの間でやり取りされるCookie自体を守る設定も非常に重要です。

Webサーバー(ApacheやNginxなど)やフレームワークの設定で、以下の属性がCookieに付与されているかを必ず確認しましょう。

  • HttpOnly 属性
  • 意味:JavaScriptからCookie(セッションID)を読み取れないようにする設定です。万が一、サイトにXSS(クロスサイトスクリプティング)の脆弱性があったとしても、攻撃者にセッションIDを盗み出されるリスクを劇的に減らせます。
  • Secure 属性
  • 意味:暗号化された通信(HTTPS)の時だけに、Cookieをブラウザから送信させる設定です。HTTP(暗号化されていない通信)でセッションIDが盗聴されるのを防ぎます。
  • SameSite 属性(Lax または Strict)
  • 意味:外部サイトからの不正なリクエスト(CSRFなど)と一緒にセッションCookieが送信されるのを防ぎます。

PHPのコードであれば、php.ini やアプリケーションの初期化ファイルで以下のように設定するのがモダンなスタンダードです。

// セッションCookieのセキュリティ設定を厳格化する例(PHP)
ini_set('session.cookie_httponly', '1'); // JavaScriptからのアクセスを禁止
ini_set('session.cookie_secure', '1');   // HTTPS通信でのみCookieを送信
ini_set('session.use_strict_mode', '1'); // 未初期化のセッションID受け入れを拒否

—

まとめ:安全なアプリケーションを作るために

セッション固定化攻撃は、仕組み自体はシンプルですが、対策を怠るとアカウント乗っ取りという重大なインシデントにつながる怖い脆弱性です。

今日覚えておいてほしいポイントは以下の2つだけです!

1. ログインが成功したときは、必ずセッションIDを新しく再生成する(session_regenerate_id などを使う)。
2. セッションIDを保存するCookieには、HttpOnly や Secure などの防御バリアをしっかり張る。

セキュリティの対策は、難しく考えすぎず、一つひとつの仕組みを丁寧に押さえていけば確実に強固なシステムを作ることができます。一緒に一歩ずつ、安全なWeb開発を進めていきましょう!

コメント

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