セッションテーブル設計 - RDBでログイン状態を管理する

中級 | 12分 で読める | 2026.06.27

公式ドキュメント

セッションをRDBで管理する理由

セッション管理というとRedisを思い浮かべることが多いですが、RDBで管理する設計もあります。

RDBセッションは、端末管理、全端末ログアウト、監査、管理画面との相性がよい方式です。

向いている場面:

  • 管理画面でログイン端末一覧を出したい
  • 全端末ログアウトを確実にしたい
  • 誰がいつログインしたかを追いたい
  • セッション失効理由を残したい
  • Redisを増やしたくない小規模構成

高速性だけを見るとRedisが有利ですが、管理性ではRDBが扱いやすい場面があります。

基本テーブル

例としてPostgreSQLで考えます。

CREATE TABLE sessions (
  id UUID PRIMARY KEY,
  user_id BIGINT NOT NULL REFERENCES users(id),
  session_hash TEXT NOT NULL UNIQUE,
  device_label TEXT,
  ip_address INET,
  user_agent_hash TEXT,
  auth_level TEXT NOT NULL DEFAULT 'password',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  last_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  expires_at TIMESTAMPTZ NOT NULL,
  revoked_at TIMESTAMPTZ,
  revoked_reason TEXT
);

Cookieに入れるセッションIDをそのままDBに保存せず、ハッシュ化した値を保存する設計もあります。

Cookie: session_id=raw_random_value
DB: session_hash=hash(raw_random_value)

DB漏洩時に、そのままCookieとして使われるリスクを下げられます。

有効なセッションの条件

ログイン済み判定では、少なくとも次を確認します。

SELECT *
FROM sessions
WHERE session_hash = $1
  AND revoked_at IS NULL
  AND expires_at > now();

sessions テーブルに行があるだけでは有効とは限りません。

意味
expires_at期限切れか
revoked_at明示的に失効されたか
auth_levelどの認証レベルか
last_seen_at最近使われたか

論理削除のように revoked_at を残すと、監査や不正調査に使えます。

インデックス

認証チェックでは、session_hash で1件を引くことが多いです。

CREATE UNIQUE INDEX idx_sessions_session_hash
ON sessions(session_hash);

ユーザーごとのセッション一覧には、次を使います。

CREATE INDEX idx_sessions_user_active
ON sessions(user_id, revoked_at, expires_at);

期限切れ掃除には、expires_at が役立ちます。

CREATE INDEX idx_sessions_expires_at
ON sessions(expires_at);

ただし、インデックスを貼りすぎると書き込みが重くなります。セッションはログイン、アクセス更新、ログアウトで更新されるため、必要なクエリから逆算します。

last_seen_at の更新頻度

すべてのリクエストで last_seen_at を更新すると、DB書き込みが増えます。

避けたい実装:

全リクエストで UPDATE sessions SET last_seen_at = now()

実務では、次のように間引きます。

最終更新から5分以上経っていたら更新する

また、画像やCSSでは更新しない、認証APIだけで更新する、といった制御もできます。

全端末ログアウト

全端末ログアウトは、RDBだと分かりやすく書けます。

UPDATE sessions
SET revoked_at = now(),
    revoked_reason = 'logout_all'
WHERE user_id = $1
  AND revoked_at IS NULL;

この端末だけログアウト:

UPDATE sessions
SET revoked_at = now(),
    revoked_reason = 'user_logout'
WHERE session_hash = $1
  AND revoked_at IS NULL;

パスワード変更後に全失効する場合:

UPDATE sessions
SET revoked_at = now(),
    revoked_reason = 'password_changed'
WHERE user_id = $1
  AND revoked_at IS NULL;

revoked_reason があると、後から「なぜログアウトされたのか」を追いやすくなります。

device管理

端末一覧を出す場合、セッション行に端末情報を持たせます。

Chrome on macOS
Safari on iPhone
Edge on Windows

ただし、User-Agent文字列をそのまま信用しすぎないようにします。User-Agentは偽装できますし、長くて扱いにくい値です。

保存例:

  • 表示用の短い device_label
  • 調査用の user_agent_hash
  • 最終利用時刻
  • おおまかなIP

IPアドレスは個人情報に近い扱いになるため、保存期間や利用目的を明確にします。

期限切れデータの掃除

期限切れや失効済みセッションは、無限に残さないようにします。

DELETE FROM sessions
WHERE expires_at < now() - interval '30 days'
   OR revoked_at < now() - interval '30 days';

ただし、監査要件がある場合は、セッション本体を削除し、別の監査ログに必要最小限を残す方がよいこともあります。

Redisとの使い分け

観点RedisRDB
読み書き速度高速DB負荷に注意
TTL得意掃除処理が必要
管理画面別設計が必要得意
監査弱め得意
全端末ログアウトSet管理などが必要SQLで書きやすい

大規模で高頻度ならRedis、管理性や監査を重視するならRDB、という判断になります。両方を組み合わせる設計もあります。

まとめ

RDBでセッションを管理すると、端末管理や監査、全端末ログアウトを設計しやすくなります。

  • sessions テーブルに期限と失効状態を持たせる
  • Cookieの生値ではなくハッシュ保存を検討する
  • expires_atrevoked_at を分ける
  • last_seen_at の更新は間引く
  • ユーザー単位の全端末ログアウトをSQLで実装できる
  • 古いセッションの掃除方針を決める

セッションは認証の土台なので、ただ保存するだけでなく、失効・監査・端末管理まで含めて設計します。

参考リソース

次に読む記事

← 一覧に戻る
PR
PR
PR
PR