Webアプリケーションにおけるセッション管理を、安全かつ運用しやすく設計・実装するための要点をまとめたものです。


目次


1. セッションIDの生成

項目推奨
生成方法暗号論的に安全な乱数生成器(CSPRNG)を使う
エントロピー64bit以上(実用上は128bit以上を推奨)
中身意味を持たない値にする(ユーザーID・時刻・連番などを含めない)
自作避ける。フレームワーク標準のセッション機構を使う
  • セッションIDの名前は PHPSESSID や JSESSIONID などデフォルト名から変更し、使用技術を推測されにくくする。
  • 推測可能な値(連番、タイムスタンプ、ユーザー名のハッシュなど)は使わない。

2. セッションIDの受け渡し

  • Cookieで受け渡す。URLパラメータ(?sid=...)やhiddenフィールドには載せない。
    • URLに含めると、Refererヘッダ、ブラウザ履歴、アクセスログ、共有リンクから漏洩する。
  • サーバー側は、自分が発行していないセッションIDを受け付けない(strict mode)。

3. Cookie属性

Set-Cookie: __Host-sid=<random>; Path=/; Secure; HttpOnly; SameSite=Lax
属性目的
SecureHTTPS通信でのみ送信する
HttpOnlyJavaScriptから読めなくし、XSSによる窃取を防ぐ
SameSite=Lax / Strictクロスサイトリクエストでの送信を制限し、CSRFを緩和する
Path必要な範囲に限定する
Domain原則指定しない(サブドメインへの送信を防ぐ)
__Host- プレフィックスSecure・Path=/・Domainなしを強制し、サブドメインからの上書きを防ぐ
Expires / Max-AgeセッションCookieは原則指定しない(ブラウザ終了で破棄)。「ログイン状態を保持」は別設計にする

4. ライフサイクル管理

4.1 ログイン時の再生成(セッション固定攻撃対策)

  • 認証成功時に必ずセッションIDを再生成する。
  • 権限が変わるタイミング(管理者モードへの切り替え、パスワード変更、MFA完了など)でも再生成する。
  • 古いセッションIDはサーバー側で確実に無効化する。

4.2 タイムアウト

種類内容目安
アイドルタイムアウト最終操作から一定時間で失効高リスク: 2〜5分 / 一般: 15〜30分
絶対タイムアウトログインから一定時間で強制失効4〜8時間(業務アプリ)など用途次第
  • タイムアウトはサーバー側で判定する(クライアント側のタイマーだけに頼らない)。

4.3 ログアウト

  • サーバー側のセッションを破棄する(Cookieを消すだけでは不十分)。
  • クライアントのCookieも削除する(Max-Age=0)。
  • 必要に応じて Clear-Site-Data: "cookies", "storage" ヘッダを返す。
  • ログアウトボタンはどの画面からも見つけやすい位置に置く。

4.4 強制失効

  • パスワード変更・リセット時は、そのユーザーの他の全セッションを失効させる。
  • ユーザーが自分のアクティブセッション一覧を確認し、個別にログアウトできる機能を提供する。
  • 管理者がアカウント単位でセッションを強制失効できるようにする。

5. サーバー側のセッションストア

  • セッションデータはサーバー側に保存し、クライアントにはIDのみを渡す。
  • 保存先は Redis / DB など、複数台構成でも共有できるストアを使う。
    • TTLをストア側にも設定し、期限切れデータが残らないようにする。
  • セッションに保存するデータは最小限にする(パスワードやカード番号などは入れない)。
  • セッションIDそのものをDBに保存する場合は、ハッシュ化して保存すると漏洩時の被害を抑えられる。

6. トークンベース(JWTなど)を使う場合

JWTは「ステートレス」が利点ですが、セッション管理として使う場合は以下に注意します。

  • 失効が難しい:発行済みトークンを即時無効化できない。
    • アクセストークンは短命(5〜15分)にし、リフレッシュトークンで更新する。
    • リフレッシュトークンはサーバー側で管理し、ローテーション(使用ごとに再発行・旧トークン失効)と再利用検知を行う。
  • 保存場所:localStorage はXSSで読まれるため避け、HttpOnly Cookieに保存するのが無難。
  • 署名検証:alg: none を拒否し、許可するアルゴリズムを固定する。exp / iss / aud を必ず検証する。
  • 機密情報をペイロードに入れない(署名されているだけで暗号化はされていない)。
  • 「単一のWebアプリのログイン状態を保つ」だけなら、従来のサーバー側セッションの方がシンプルで安全なことが多い。

7. 関連する攻撃と対策

攻撃対策
セッションハイジャック(ID窃取)HTTPS必須、Secure・HttpOnly、XSS対策、HSTS
セッション固定(Session Fixation)ログイン時のID再生成、未発行IDの拒否
CSRFSameSite属性、CSRFトークン、Originヘッダ検証
XSS経由のCookie窃取HttpOnly、出力エスケープ、CSP
ブルートフォースによるID推測十分な長さのランダムID、異常アクセスのレート制限
共有端末での残存適切なタイムアウト、ログアウト時の確実な破棄、Cache-Control: no-store

8. 追加の防御策

  • HTTPSを全ページで強制し、HSTS(Strict-Transport-Security)を設定する。
  • 認証後のページには Cache-Control: no-store を付け、ブラウザやプロキシにキャッシュさせない。
  • 重要操作(パスワード変更、送金、メールアドレス変更など)の前に再認証を求める。
  • セッションと一緒にUser-Agentなどを記録し、急な変化があれば再認証を求める(IPアドレスはモバイル回線などで頻繁に変わるため、厳密な一致を条件にするのは慎重に)。
  • 同時ログイン数の制限や、新しい端末からのログイン通知を検討する。

9. ログと監視

  • 記録するもの:ログイン成功/失敗、ログアウト、セッション再生成、失効、タイムアウト、不正なセッションIDの受信。
  • セッションIDそのものをログに出さない(出す場合はハッシュ化した値にする)。
  • 不正なIDの大量送信、短時間での多地点アクセスなど異常パターンを検知・アラートする。

チェックリスト

  • フレームワーク標準のセッション機構を使っている
  • セッションIDはCSPRNGで生成され、十分な長さがある
  • セッションIDをURLに含めていない
  • Cookieに Secure / HttpOnly / SameSite を設定している
  • __Host- プレフィックスを使っている(可能な場合)
  • ログイン時・権限変更時にセッションIDを再生成している
  • アイドルタイムアウトと絶対タイムアウトをサーバー側で実装している
  • ログアウト時にサーバー側セッションを破棄している
  • パスワード変更時に他のセッションを失効させている
  • 全ページHTTPS + HSTSを設定している
  • CSRF対策を実装している
  • 認証後ページに Cache-Control: no-store を付けている
  • 重要操作の前に再認証を求めている
  • セッションIDをログに平文で出力していない
  • (JWTの場合)短命アクセストークン + リフレッシュトークンのローテーションを行っている

参考資料

0件のページ