#はじめに
「ログイン後、別のページに移動してもログイン状態が維持されるのはなぜ?」——その答えがセッション管理です。
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"
|<----- ようこそ、管理者さん ---------|
#ポイント
- セッションIDだけをCookieに保存: ユーザー情報そのものはサーバーに保存
- セッションIDは識別子: 「このリクエストはこのユーザーのもの」と特定するための鍵
- セッションデータはサーバーで管理: 改ざんの心配がない
#セッション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が盗まれると、攻撃者がそのユーザーになりすませます。
#盗まれる経路
- 盗聴: HTTP通信の傍受 → Secure属性で対策
- XSS: JavaScriptでCookie取得 → HttpOnly属性で対策
- ログ漏洩: 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とは異なるアプローチで、トークン自体に情報を含める方式です。それぞれのメリット・デメリットを比較できるようになります。