【入門編】 セッション固定攻撃(Session Fixation)のメカニズムと対策 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

こんにちは!レッドチームエンジニアの〇〇です。

今日は、なんだか物騒な響きの「セッション固定攻撃」というテーマについて、新人IT担当者さんや、これからセキュリティの面白さに触れていきたい開発者さんに向けて、とことん優しく解説していきたいと思います。

「攻撃」なんて聞くと、ちょっと身構えちゃいますよね。でも大丈夫!セキュリティは、私たちの身近な「家の防犯」とよく似ています。泥棒の手口を知って、しっかり鍵をかける。その積み重ねなんですよ。

さあ、一緒にセキュリティの第一歩を踏み出しましょう!

—

泥棒が「偽の鍵」を渡してくる!?セッション固定攻撃って何?

まず、「セッション固定攻撃」という言葉を聞いた時、「セッションって何?」「固定ってどういうこと?」って思うかもしれません。大丈夫、そこから紐解いていきましょう。

ウェブサイトの「記憶力」:セッションって何?

私たちは普段、Amazonでお買い物をしたり、SNSにログインしたりしていますよね。

  • 買い物カゴに商品を入れたら、別のページを見てもその商品が残っている。
  • 一度ログインしたら、しばらくはログインしっぱなしでいられる。

これって、ウェブサイトが私たちを「覚えてくれている」からできることなんです。この「覚えておく」仕組みのことを、専門用語で「セッション」と呼びます。

ウェブサイトは、私たち一人ひとりに専用の「セッションID」というユニークな識別子を発行しています。これは例えるなら、「あなた専用の入館証」のようなものです。ウェブサイト側はこの入館証を見て、「ああ、この人は〇〇さんね!」と判断し、あなたの買い物カゴやログイン状態を管理しているわけです。

泥棒の手口:セッション固定攻撃のメカニズム

さて、ここからが本題の「セッション固定攻撃」です。これはですね、先ほどの「入館証」の例で言うと、「泥棒が、自分で用意した偽の入館証を、あなたに強引に使わせる」ような手口なんです。

具体的なシナリオを見てみましょう。ちょっと怖い話ですが、これが攻撃者の思考回路です。

1. 【泥棒、下見をする】

  • まず、攻撃者(泥棒)がターゲットのウェブサイトにアクセスします。
  • すると、ウェブサイトは攻撃者にも「セッションID」(入館証)を発行しますよね。攻撃者はこの「入館証」をひっそりと記録しておきます。これが「偽の入館証」になります。まだ誰もログインしていない、空っぽの状態の入館証です。

2. 【泥棒、偽の入館証を渡す】

  • 次に、攻撃者はあなた(被害者)に、罠を仕掛けたURLを送ります。このURLには、先ほど攻撃者が取得した「偽の入館証(セッションID)」が仕込まれています。
  • 「新製品発表!ここだけの特別情報!」なんて甘い言葉で、URLをクリックさせようとします。

3. 【あなたが、偽の入館証でログインする】

  • あなたは何も知らずにそのURLをクリックし、ウェブサイトにアクセスします。
  • すると、ウェブサイトはURLに仕込まれた「偽の入館証」を受け取って、「ああ、この入館証で来た人ね」と認識します。
  • そして、あなたは普段通り、ユーザー名とパスワードを入力してログインします。

【ここが盲点!】

  • 実はこの時、多くのウェブサイトでは、ログインしてもセッションID(入館証)を新しいものに交換しない場合があります。つまり、あなたがログインしたのに、泥棒があなたに渡した「偽の入館証」をそのまま使い続けてしまうんです!

4. 【泥棒、あなたになりすまして侵入!】

  • 攻撃者は、事前に記録しておいた「偽の入館証」を使い、もう一度ウェブサイトにアクセスします。
  • ウェブサイトは、その入館証を見て「あれ?この入館証は、さっき〇〇さん(あなた)がログインしたばかりだぞ。よし、〇〇さんとして扱うか!」と勘違いしてしまいます。
  • 結果、攻撃者はあなたになりすまして、あなたの個人情報を見たり、勝手に買い物をしたり…といった悪事ができてしまう、というわけです。

なんだか、背筋が寒くなるような手口ですよね。
「鍵を渡され、それが正規の鍵として使われた挙げ句、泥棒がその鍵を使って侵入してくる」という、まさに家の鍵の例えがぴったりくる攻撃です。

どうすればいいの?重要な対策は「鍵の交換」と「セキュリティ強化」!

このセッション固定攻撃を防ぐための対策は、大きく分けて2つあります。どちらも、泥棒から家を守るための基本的な防犯対策と共通していますよ!

対策その1:ログインしたら「新しい鍵」に交換!セッションIDの再生成

先ほどのシナリオの盲点は、「ログインしてもセッションIDが変わらないこと」でしたよね。だったら簡単です。

「ログインに成功したら、古いセッションIDを破棄して、新しいセッションIDを生成する」

これを徹底すれば良いんです!これは、「ログインしたら、それまで使っていた入館証を破棄して、新しい入館証を発行し直す」イメージです。泥棒が持っているのは古い入館証なので、新しい入館証では侵入できませんよね。

多くのプログラミング言語やフレームワークには、セッションIDを再生成するための関数が用意されています。ここではPHPを例に見てみましょう。

<?php
// PHPのセッションを開始
session_start();

// ユーザーからのログイン情報を処理する部分
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username']) && isset($_POST['password'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    // ここでデータベースなどを使ってユーザー名とパスワードを検証
    if (authenticate_user($username, $password)) { // ログイン成功と仮定
        
        // --- ★ココが重要!セッションIDの再生成★ ---
        session_regenerate_id(true); // true を指定すると、古いセッションを削除します
        // ------------------------------------------

        // セッションにログイン情報を保存
        $_SESSION['loggedin'] = true;
        $_SESSION['username'] = $username;

        echo "ログインに成功しました!新しいセッションIDで保護されています。";
        // ログイン後のページにリダイレクトするなど
        header('Location: /dashboard.php');
        exit;
    } else {
        echo "ユーザー名またはパスワードが間違っています。";
    }
}

// ユーザー認証関数(ダミー)
function authenticate_user($username, $password) {
    // 実際にはデータベースと照合するロジックをここに書きます
    return ($username === 'testuser' && $password === 'password123');
}
?>

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ログインページ</title>
</head>
<body>
    <h2>ログイン</h2>
    <form action="" method="post">
        <label for="username">ユーザー名:</label><br>
        <input type="text" id="username" name="username"><br><br>
        <label for="password">パスワード:</label><br>
        <input type="password" id="password" name="password"><br><br>
        <input type="submit" value="ログイン">
    </form>
</body>
</html>

このコードのポイントは、session_regenerate_id(true); という一行です。
true を渡すことで、古いセッションデータも安全に破棄してくれるので、より安心ですね。

対策その2:Cookie属性を賢く使って「家のセキュリティシステム」を強化!

セッションIDは、ほとんどの場合、あなたのブラウザに「Cookie」という形で保存されています。このCookieに対しても、適切なセキュリティ設定をしてあげることで、さらに防御力を高めることができます。

Cookieの属性は、例えるなら「家の鍵のかけ方」や「家のセキュリティシステム」の設定のようなものです。これをしっかり設定しないと、せっかく良い鍵を持っていても、窓が開けっぱなしだったり、鍵の受け渡しが安全ではなかったりしてしまいます。

特に重要な3つの属性を見ていきましょう。

1. HttpOnly 属性:JavaScriptからのアクセス禁止(窓から侵入させない!)

  • 意味: この属性を設定すると、CookieはJavaScriptからアクセスできなくなります。
  • なぜ重要?: もしウェブサイトに脆弱性(例えばXSS攻撃)があって、悪意のあるJavaScriptが実行されてしまったとしても、HttpOnly が設定されていれば、そのJavaScriptはセッションID(Cookie)を盗み出すことができません。
  • 例え: 家の鍵は、必ずドアからしか使えず、窓から手を入れて鍵を開ける、というような行為を許さないようにするイメージです。

PHPでの設定例:
php.ini で設定するか、session_set_cookie_params() 関数で設定します。

<?php
// session_set_cookie_params() を使ってセッションクッキーの属性を設定する例
// セッション開始前に呼び出す必要があります
session_set_cookie_params([
    'lifetime' => 0,          // セッションクッキーの有効期限(0はブラウザを閉じると期限切れ)
    'path' => '/',            // クッキーが有効なパス
    'domain' => '',           // クッキーが有効なドメイン
    'secure' => true,         // HTTPS通信でのみクッキーを送信
    'httponly' => true,       // JavaScriptからのアクセスを禁止
    'samesite' => 'Lax'       // SameSite属性の設定
]);

session_start();
// ... 後のセッション処理 ...
?>

もしくは、php.ini ファイルに直接記述する方法もあります。

; php.ini の設定例
session.cookie_httponly = 1

2. Secure 属性:HTTPS通信のみに制限(金庫の中で鍵を渡す!)

  • 意味: この属性を設定すると、CookieはHTTPS(暗号化された通信)の場合にのみブラウザからサーバーに送信されます。
  • なぜ重要?: もしHTTP(暗号化されていない通信)でセッションIDが送信されてしまうと、通信経路の途中で傍受されて、セッションIDが盗み見られる可能性があります(中間者攻撃)。Secure を設定することで、このリスクを防ぎます。
  • 例え: 大切な鍵の受け渡しは、必ず頑丈な金庫の中で行う、というイメージです。盗み見られる心配がありません。

PHPでの設定例: session_set_cookie_params() 関数で設定するか、php.ini で設定します。

<?php
session_set_cookie_params([
    // ... 他の属性 ...
    'secure' => true,         // ★ココが重要!HTTPS通信でのみクッキーを送信
    // ... 他の属性 ...
]);
session_start();
?>

または php.ini で設定:

; php.ini の設定例
session.cookie_secure = 1

注意: Secure 属性を設定すると、HTTP通信ではセッションが使えなくなります。必ずサイト全体をHTTPS化してから設定するようにしてくださいね。

3. SameSite 属性:クロスサイトリクエストの制限(信頼できる人だけに鍵を貸す!)

  • 意味: この属性は、Cookieがクロスサイトリクエスト(別のドメインからのリクエスト)と一緒に送信されるかどうかを制御します。
  • なぜ重要?: 主にCSRF(クロスサイトリクエストフォージェリ)攻撃の対策として非常に有効ですが、セッション関連の攻撃全般に対する防御力を高めます。
  • 例え: あなたの家の鍵を、信頼できる家族や友人(同じサイトからのリクエスト)には貸すけど、見知らぬ人(別のサイトからのリクエスト)には絶対に貸さない、というような設定です。

SameSite 属性には、主に3つの値があります。

  • Lax (推奨): ほとんどのクロスサイトリクエストではCookieを送信しませんが、安全なトップレベルのナビゲーション(リンクをクリックして移動するなど)では送信します。普段使いに最もバランスが良い設定です。
  • Strict: 全てのクロスサイトリクエストでCookieを送信しません。セキュリティは高いですが、他のサイトからのリンクで自分のサイトに飛んできた場合などでもログイン状態が維持されないため、使い勝手が悪くなることがあります。
  • None: 全てのクロスサイトリクエストでCookieを送信します。この場合、Secure 属性も同時に設定する必要があります。ただし、セキュリティ的には最も緩い設定なので、特別な理由がない限りは推奨されません。

PHPでの設定例: session_set_cookie_params() 関数で設定するか、php.ini で設定します。

<?php
session_set_cookie_params([
    // ... 他の属性 ...
    'samesite' => 'Lax'       // ★ココが重要!SameSite属性をLaxに設定
]);
session_start();
?>

または php.ini で設定:

; php.ini の設定例
session.cookie_samesite = "Lax"

まとめ:セキュリティは「一歩ずつ」の積み重ね!

セッション固定攻撃、ちょっと怖い内容でしたが、そのメカニズムと対策を理解できましたでしょうか?

まとめると、セッション固定攻撃を防ぐための最重要ポイントは以下の2つです。

1. ログイン成功時にセッションIDを必ず再生成する!(古い鍵は使わせない!)
2. セッションCookieの属性を適切に設定する! (HttpOnly, Secure, SameSite で家のセキュリティを強化!)

セキュリティは、まるで泥棒との「いたちごっこ」のような側面もあります。攻撃者は常に新しい手口を考えてきますが、私たちもそれに負けず、知識をアップデートし、適切な対策を講じることが重要です。

今日学んだことは、ウェブアプリケーションのセキュリティを守るための非常に基本的な、しかし極めて重要な知識です。これを機に、皆さんの開発やインフラ構築に、ぜひセキュリティの視点を取り入れてみてください。

「完璧なセキュリティ」というものは存在しませんが、「一歩ずつ、確実に安全なシステムを構築していく」ことは可能です。皆さんの学びを応援しています!

—
【補足】
ウェブフレームワーク(Laravel, Ruby on Rails, Djangoなど)を利用している場合、多くはこれらのセッションID再生成やCookie属性の設定を、フレームワーク側で自動的に、あるいは簡単な設定で実現できるようになっています。フレームワークのドキュメントをよく読み、推奨されるセキュリティ設定を適用するようにしましょう。

コメント

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