2026-08-10 セッションの有効期限と SESSION_PASSWORD の扱い(Issue #41)
- 課題:
lib/session.tsがttlもcookieOptions.maxAgeも指定しておらず、有効期限が iron-session の既定値任せになっていた。その値がどこにも文書化されておらず、SESSION_PASSWORDを変更したときに全員がサインアウトされることも書かれていなかった。
調べた結果
実際の Set-Cookie(GET /api/auth/nonce)は次のとおり。
initiation_session=<sealed>; Path=/; Expires=...; Max-Age=1209540; HttpOnly; SameSite=lax| 項目 | 実際の挙動 |
|---|---|
| 有効期限 | 14日(Max-Age=1209540 = 14日 − 60秒のずれ吸収) |
| 期限の更新 | されない。通常のページ表示では session.save() を呼ばないため、サインイン時点からの絶対期限 |
| サーバー側の検証 | される。封緘データ自体が期限を持ち、unsealData は Expired seal を空セッションとして返す |
期限切れは「サインインしていない」側に倒れる(fail closed)。実装上の不備はなかった。
決定
1. 14日を採用し、lib/session.ts に明示する
既定値と同じ値だが、SESSION_TTL_SECONDS として明示的に渡す。
- 既定値のままだと、iron-session の更新で既定が変わったときに挙動が動いても気づけない。
- 値がコード上に見えることで、文書との対応も確認できる。
2. 管理者のセッションを短くはしない
/admin の利用頻度が分からない段階で運営の操作を止めるほうが実害が大きい。ロール別のセッション寿命は、ロール分離そのもの(#17)を扱うときに再検討する。
3. SESSION_PASSWORD は単一の文字列のまま。ローテーション手順は文書で担保する
iron-session は複数パスワードのマップ({ 2: 新, 1: 旧 })を受け取れるため、旧パスワードで封緘されたセッションを保ったまま鍵を切り替えることもできる。今回はこれを採らない。
- 環境変数の形式を変える必要があり(現在は単一の文字列)、鍵をいつ捨てるかという運用も増える。
- 本番稼働前で、守るべき既存セッションが存在しない。
- 「変更すると全員がサインアウトされる」ことを知ったうえで変更できれば、現時点では足りる。
無停止でのローテーションが必要になるのは、本番稼働後に鍵の入れ替えを定期実施する方針を採ったときで、そのときに改めて判断する。
文書化した場所
docs/guide/architecture.md: セッションの節を独立させ、期限・起点・Cookie属性・サーバー側でも効くことを表で示したdocs/reference/environment.md:SESSION_PASSWORDの変更で既存セッションが無効になることを追記docs/runbook-manual-operations.md: 運用上の注意に、変更時の告知と有効期限を追記docs/guide/troubleshooting.md: 「サインインしていたはずなのに、サインインを求められる」を追加
含めなかったもの
- サインイン状態の表示とサインアウト導線の修正(#40)
- セッションストアをCookieからDBへ移すこと