#はじめに

「ログイン後、別のページに移動してもログイン状態が維持されるのはなぜ?」——その答えがセッション管理です。

HTTPはステートレスなプロトコルです。サーバーは各リクエストを独立して処理し、「前のリクエストを送ってきたのと同じユーザーだ」という情報を持ちません。セッション管理は、この問題を解決するための仕組みです。

この記事を読むと、以下のことができるようになります:

  • セッション管理の仕組みを理解できる
  • セッションIDの役割と安全な取り扱いがわかる
  • セッション固定攻撃とその対策を理解できる

#セッションとは

セッションとは、ユーザーとWebアプリケーション間の一連のやり取りを表す概念です。

ログインからログアウトまで、または一定時間内の操作を「1つのセッション」として管理します。

ログイン → ダッシュボード → 設定 → ログアウト
└───────────── 1つのセッション ─────────────┘

#サーバーサイドセッションの仕組み

最も一般的なセッション管理方式は「サーバーサイドセッション」です。

#基本的な流れ

[ブラウザ]                           [サーバー]
    |                                    |
    |------ ログイン情報を送信 ----------->|
    |                                    |
    |                          セッションを作成
    |                          session_id: "abc123"
    |                          data: {user_id: 1, role: "admin"}
    |                          ↓ サーバーのメモリ/DBに保存
    |                                    |
    |<----- Set-Cookie: session_id=abc123 |
    |                                    |
    |------ Cookie: session_id=abc123 --->|
    |                          session_id で検索
    |                          → user_id: 1, role: "admin"
    |<----- ようこそ、管理者さん ---------|

#ポイント

  1. セッションIDだけをCookieに保存: ユーザー情報そのものはサーバーに保存
  2. セッションIDは識別子: 「このリクエストはこのユーザーのもの」と特定するための鍵
  3. セッションデータはサーバーで管理: 改ざんの心配がない

#セッションIDの要件

セッションIDは推測されてはいけません。以下の要件を満たす必要があります。

#1. 十分なランダム性

暗号論的に安全な乱数生成器を使用します。

# ❌ 危険: 連番や推測可能な値
session_id=1
session_id=user123

# ✅ 安全: 暗号論的乱数
session_id=8f7d3c2a1b9e4f6d8a2c1e3b5f7d9a1c

#2. 十分な長さ

128ビット以上のエントロピーが推奨されます。

# 例: 32文字の16進数 = 128ビット
session_id=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6

#3. 一意性

同じセッションIDが複数のユーザーに割り当てられてはいけません。

#セッションの保存場所

サーバー側でセッションデータを保存する方法はいくつかあります。

保存場所メリットデメリット
メモリ(プロセス内)高速サーバー再起動で消失、スケールしにくい
ファイル簡単I/O負荷、共有が難しい
データベース永続化、共有可能オーバーヘッド
Redis/Memcached高速、共有可能、TTL管理追加のインフラ

#複数サーバー環境での注意

ロードバランサーで複数のサーバーに分散する場合、セッションデータの共有が必要です。

[ロードバランサー]

   ┌──┴──┐
   │     │
[サーバー1][サーバー2]
   │     │
   └──┬──┘

   [Redis]  ← セッションを共有

またはスティッキーセッション(同じユーザーを常に同じサーバーに振り分ける)を使用しますが、可用性の観点から共有ストレージが推奨されます。

#セッションのライフサイクル

#1. セッションの作成

ログイン成功時にセッションを作成します。

POST /login
→ 認証成功
→ セッション作成(session_id生成、データ保存)
→ Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax

#2. セッションの維持

以降のリクエストでは、CookieのセッションIDでセッションを特定します。

GET /dashboard
Cookie: session_id=abc123
→ セッションを検索
→ ユーザー情報を取得
→ ダッシュボードを表示

#3. セッションの破棄

ログアウト時またはタイムアウト時にセッションを破棄します。

POST /logout
→ サーバー側のセッションデータを削除
→ Set-Cookie: session_id=; Max-Age=0(Cookieも削除)

#セッションタイムアウト

セキュリティのため、一定時間でセッションを無効化します。

#アイドルタイムアウト

最後の操作から一定時間経過で無効化。

最後のリクエスト: 14:00
タイムアウト: 30分
→ 14:30以降はセッション無効

#絶対タイムアウト

セッション作成から一定時間で強制的に無効化。

セッション作成: 10:00
絶対タイムアウト: 8時間
→ 18:00以降はセッション無効(操作していても)

両方を組み合わせることが推奨されます。

#セッション固定攻撃

#攻撃の仕組み

攻撃者が用意したセッションIDを被害者に使わせる攻撃です。

1. 攻撃者がサイトにアクセスし、セッションID(xyz789)を取得
2. 攻撃者が被害者に細工したリンクを送信:
   https://bank.com/login?session_id=xyz789
3. 被害者がリンクをクリックし、ログイン
4. 被害者のセッションIDは xyz789(攻撃者が知っている!)
5. 攻撃者が xyz789 でアクセス → 被害者としてログイン状態

#対策: ログイン時のセッションID再生成

ログイン成功時に、新しいセッションIDを発行します。

POST /login
→ 認証成功
→ 古いセッションを破棄
→ 新しいセッションIDを生成(abc123)
→ Set-Cookie: session_id=abc123

これにより、攻撃者が知っている古いセッションIDは無効になります。

#セッションハイジャック

セッションIDが盗まれると、攻撃者がそのユーザーになりすませます。

#盗まれる経路

  1. 盗聴: HTTP通信の傍受 → Secure属性で対策
  2. XSS: JavaScriptでCookie取得 → HttpOnly属性で対策
  3. ログ漏洩: URLにセッションIDが含まれる → Cookieで管理

#対策

  • Secure属性、HttpOnly属性を必ず設定
  • セッションIDをURLに含めない
  • 重要な操作時は再認証を要求

#実装例(概念)

#Express.js + express-session の例

const session = require('express-session');

app.use(session({
  secret: 'your-secret-key',
  name: 'sessionId',  // Cookie名
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true,     // HTTPS only
    httpOnly: true,   // JavaScript不可
    sameSite: 'lax',  // CSRF対策
    maxAge: 3600000   // 1時間
  }
}));

// ログイン時のセッション再生成
app.post('/login', (req, res) => {
  // 認証処理...
  req.session.regenerate((err) => {
    req.session.userId = user.id;
    res.redirect('/dashboard');
  });
});

// ログアウト時のセッション破棄
app.post('/logout', (req, res) => {
  req.session.destroy((err) => {
    res.clearCookie('sessionId');
    res.redirect('/login');
  });
});

#セッションデータの保存例

// セッションにデータを保存
req.session.userId = 123;
req.session.role = 'admin';
req.session.cart = ['item1', 'item2'];

// セッションからデータを取得
const userId = req.session.userId;

#まとめ

  • セッション管理はステートレスなHTTPに状態を持たせる仕組み
  • サーバーサイドセッション: セッションIDをCookieに、データをサーバーに保存
  • セッションIDの要件: 十分な長さ、ランダム性、一意性
  • セッション固定攻撃: ログイン時にセッションIDを再生成して対策
  • セッションハイジャック: Secure、HttpOnly、SameSite属性で対策
  • ログアウト時はサーバー側のセッションも削除

#次のステップ

サーバーサイドセッションの仕組みを理解したところで、次はJWT(JSON Web Token)について学びましょう。JWTはセッションIDとは異なるアプローチで、トークン自体に情報を含める方式です。それぞれのメリット・デメリットを比較できるようになります。

#参考リンク