Session
Overview
一般的に、アプリケーションを使用する最初のステップは認証。
エンドユーザーは自分の身元を証明するための資格情報を提供し、成功後にログインする。
このステップの後、アイデンティティシステム(例: アイデンティティプロバイダー、認証サーバーなど)は、ユーザーが誰であり、どのリソースにアクセスできるかを認識する。
HTTP は本質的にステートレスであるため、セッション内の各リクエストは独立しており、以前の情報を記憶しない。それが認証に関しては問題となる。
ユーザーが毎回アクションを実行する度に再認証をするのは面倒であり、ユーザーエクスペリエンスを損なう。
ここに登場するのが、セッションベースの認証とJWT (JSON Web Tokens) 認証。
この2つは認証状態を維持するための人気のある方法。
それぞれに独自の利点とトレードオフがあり、どちらを選ぶかはアプリケーションの特定のニーズに依存する。
もし2つのうちどちらを選ぶか迷っているなら、このガイドを見る
JWT とセッション認証の比較
- 各プログラム言語にはメジャーなセッションライブラリがあり、セッションID生成には、それらを使うことが多い
- それらのライブラリの多くのデフォルトの実装は、セッションIDとしてユニークで推測しずらい値を生成し、それをキーとして「ユーザーID」などのユーザーデータを紐付けて、ローカルファイルやメモリ上にキーバリューデータとして保存しておく
- そして、ブラウザーからアクセスがあった場合、クッキーからセッションIDを読み取りセッションIDをキーに、キーバリューデータから該当するユーザーデータを復元する
- キーバリューデータをサーバーのローカルファイルやメモリに保存するため、サーバーを複数設置して負荷分散させた場合、接続毎に接続先のサーバーが変わってしまうので、セッションが維持できなくなってしまう
- なので、どのサーバーにアクセスしても適切なユーザーデータが復元できるように、キーバリューデータをローカルではなく、データベースに格納することが多い。
セッションはCookieを使って実現するのが一般的 HTTPセッションは通常クッキーを利用して行います。クッキーを利用したセッションの場合、お薦めする設定は以下の通りです。
-
ドメイン名は指定しない
-
パスはルート(/)を指定する パスですが、アプリの中にはURIパスを検出してパスを設定してクッキーを送るようにしている物もあります。ルート(/)とサブディレクトリ(/app1/など)にクッキーが設定されている場合、ルートの方が優先されるので安全面を考えるとルートの方が良いです。
-
セッション管理用のクッキーはセッションクッキー(有効期間0)にする 常識として知っておくべき知識です。クッキーの有効期限が0の場合、普通のブラウザはクッキーをメモリにみ保存します。 つまりブラウザを終了させるとクッキーが消えます。セッション管理には必ず有効期限0を指定すべきです。
-
httponly属性を付ける
-
可能な場合は必ずsecure属性をつける
-
複数アプリケーションを利用する場合はsession.nameまたはsession_name()でセッションクッキー名で指定する(アプリケーションzの固有名デフォルトで設定し、設定項目として変更できるようにする)
HTTP通信の問題点
- HTTP通信は1回1回の単発の通信のため接続を維持できない。
- HTTP通信はユーザー識別の仕組みがないので、どのユーザーからの接続かを判別できない。なので、一連のHTTP通信で、ユーザー接続を維持し続けるには、何かしらの仕組みを実装する必要がある
解決方法
ユーザー認証完了後、サーバー側でユーザー識別子を発行しブラウザへのレスポンスで識別子を送る。
次回以降ブラウザからサーバーにアクセスする際、毎回その識別子をサーバーに送ることにより、サーバー側でどのユーザーからのアクセスかがわかるようにする。
そして、その識別子のやり取り方法の1つとして、クッキーを用いる方法がある。
セッション保護するために必要なこと
セッションを保護するためのベストプラクティスには、以下のようなものがあります。
セッションIDの強化: セッションIDは予測不可能である必要があります。高いエントロピーを持つランダムな値を使用し、セッションIDの生成には信頼できる暗号ライブラリを使用してください。 セッションの有効期限管理: セッションは適切なタイミングでタイムアウトするように設定すべきです。長期間活動がない場合はセッションを自動的に終了させ、ユーザーがログアウトする際にはセッションIDを無効化してください。 セキュアな通信の使用: セッションIDを含むすべての通信は、HTTPSを通じて暗号化されるべきです。これにより、中間者攻撃によるセッションIDの盗聴を防ぎます。 Cookieのセキュリティ属性の利用: HttpOnlyおよびSecure属性をセッションCookieに設定することで、クロスサイトスクリプティング(XSS)攻撃や中間者攻撃から保護できます。 セッション固定攻撃の防止: セッションIDをユーザーがログインした後に変更することで、セッション固定攻撃を防ぎます。これにより、攻撃者が事前に設定したセッションIDを使用できなくなります。 クロスサイトリクエストフォージェリ(CSRF)保護: トークンベースのCSRF保護を実装して、ユーザーが意図しないアクションを強制されるのを防ぎます。
Session IDの生成・安全性
セッションIDの生成方法はHTTPの仕様として定まっていない。サーバー側の実装に依存する
ただしOWASPなどではベストプラクティスとして以下を定義している。
- 予測不能なランダム値を使う(CSPRNG推奨)
- 最低64ビット以上のエントロピー(理想は128ビット以上)
- 意味のない値(個人情報などを含めない)
- セッションID名は一般的な名前(例: id)に変更して、技術スタックの露出を避ける
セッションID名から、Webアプリケーションで使用されるテクノロジとプログラミング言語が明らかになる可能性がある。
Web開発フレームワークのデフォルトのセッションID名を id などの一般的な名前に変更することをお勧めしている。
Session 有効期限・自動失効
- 非アクティブ時は自動でセッションを終了(Idle timeout)
- 最大セッション時間を設定(Absolute timeout)
- ログアウト時にセッションIDを無効化
Session固定攻撃(Session Fixation)対策
ログイン後にセッションIDを再生成する(session_regenerate_id() 相当)ことで対策。
通信の保護
セッションIDが漏洩=そのユーザーになりすまし可能ってことだから、セッションIDは実質的に「鍵」。
そのため以下の対策を取り、通信を保護する。
- HTTPSを強制
- HTTP→HTTPSリダイレクト後にクッキーを設定
- HSTS(HTTP Strict Transport Security)も使うと
- Secure属性付きクッキーを使う(HTTPS通信時のみ送信される)
それでも「漏れたらどうする?」っていう前提で、短命・再生成・セキュア通信など多層防御が大事になる。
- セッションID自体の漏洩を防ぐ(通信・クッキー・スコープ管理)
- IDが漏れても短命
orすぐに失効できる設計にする
なぜセッションIDが漏れたら危険なのか?
セッションID(例: abcd1234...)は、サーバー側でそのユーザーの情報に紐づいてるのでそれさえ持っていれば、認証済みユーザーとしてふるまえる。 つまり、攻撃者がそのIDを手に入れたら以下のことができる。
- もうパスワードとかいらない
- 本人になりすましてアクセスできる
- 個人情報や設定が見られたり、勝手に操作されたりする
代表的な漏洩パターン
- クッキーのSecure属性がない → HTTP通信で漏れる(盗聴OK)
- HttpOnlyがない → XSSでJSからクッキー丸見え
- SameSite属性なし → CSRF攻撃で悪用される
- URLパラメータでセッションID渡してる(Trans SID)→ リンクやログに残って漏れる
だからこそ大事な防衛策
| 対策 | 意味 |
|---|---|
| HttpOnly | JSからアクセス不可(XSS対策) |
| Secure | HTTPS通信でしか送られない(盗聴対策) |
| SameSite | CSRF対策(クロスサイトで送られない) |
| 定期的なsession_regenerate_id() | セッション固定対策(ログイン時などにIDを再発行) |
| HTTPS強制 + HSTS | 通信全体を保護(初回アクセス含め) |
| IDに機密情報を含めない | そもそも中身は意味のないランダム文字列にする |
JWTとの違い
セッションIDはサーバー側で状態を持つ(ステートフル)けど、JWTはトークンに状態を持つ(ステートレス)から、漏れたら(復活できない・無効化できない)って意味で、JWTのセキュリティ管理がもっとシビア
セッションストレージ(DB, Redis, Cookieなど)
サーバー側でセッション情報を保持(メモリ、DB、Redisなど)するところ。
- クライアントに状態を持たせすぎない(JWTならスコープや保存期間に注意)
- セッションに個人情報を含める場合は暗号化する
- サーバー側に保存するセッションデータの中に、個人情報(PII)が含まれている場合は暗号化すべき
暗号化が必要になる具体的なケース
✅ 例1:DBにセッション情報を保存する場合。
たとえば、セッションストレージにRDB(MySQL/PostgreSQL)を使っているケースで、以下のようなデータ構造があるとする:
{
"session_id": "abc123...",
"user_id": "u_001",
"email": "<user@example.com>",
"role": "admin"
}
このとき、emailがPII(個人を特定できる情報)なので、そのフィールドを暗号化しておくのが望ましい、ということ。
暗号化対象:個人情報に該当するフィールドだけでOK(セッションID自体は一般に暗号化不要)
✅ 例2:Redisなどのインメモリストアに保存している場合。
セッションデータをそのままRedisに以下のような形式で保存してる場合:
127.0.0.1:6379> GET sess:abc123
"{\"userId\":\"u_001\",\"email\":\"user@example.com\"}"
この中身(value)を暗号化したり、Redis自体をTLS通信にして、外部から盗み見られないように保護するのが重要になる。
✅ 例3:クッキーにセッション情報を持たせている場合(クッキーストア)
Set-Cookie: session=eyJ1c2VySWQiOiAidV8wMDEifQ==; HttpOnly; Secure; ...
この場合、クッキーにユーザー情報がエンコードされているので、サーバーで署名(HMAC)や暗号化(AESなど)して改ざんや漏洩から守るべき。
暗号化が意味を持つパターン
「どうせサーバーで復号して返すなら、盗まれたら一緒じゃん」ってのは理にかなってる。
実際に、**PIIを暗号化してもセッションIDが漏れたらアウトでは?**という疑問、セキュリティ界隈でもよく議論されてる。
でも実は、それでも暗号化は「意味がある」なぜかというと、攻撃のフェーズや手段によって守れる範囲が変わるため。
1. ストレージ層の侵害
攻撃者がDBやRedisに直接アクセスできたとき。
- 暗号化してないと、
emailやuser_idなどが丸見え - 暗号化されていれば、中身を見ても意味がわからない
セッションIDを盗まれなくても、ストレージから情報が抜かれる攻撃は別経路で現実的に起こる
2. ログの誤出力やバックアップ漏洩
- セッション情報がログに誤って出力された場合(デバッグログなど)
- バックアップが外部に流出した場合
暗号化されていれば、情報漏洩時の被害が限定的になる
3. 内部のアクセス制御を細かく分離できる
- 「セッション情報の復号権限」はアプリケーション層のみに持たせる
- DB管理者が見ても、ユーザーの個人情報までは見えない
4. コンプライアンス対応(法的要件)
PIIを扱う以上、「保存時の暗号化が法的・業界的に求められる」ことが多い(例:GDPR、CCPA、ISMSなど)
Session認証でやってはいけないこと
- セッションIDを固定値や簡単な値にする
- 永続クッキーで再ログイン(Remember Me)を安易に実装する
-
有効期限の長いセッションを再ログイン用に使う 有効期限の長いセッションに良いことはない。 たとえば、公共のPCや友人のPCを使った時にブラウザを閉じてもまだログインした状態が続くのは良くありません。 自動ログインを有効にする場合は別の使い捨ての認証用クッキー(再認証用のトークン)を用い、ログインする場合に自動ログインするか、しないか選択できるようにしてユーザが制御できるようにします。ユーザが明示的にログオフした場合は再認証トークンも必ず無効にします。
-
Trans SIDを不要なのに有効にする(URLにセッションIDを含める(TransSID) Trans SID(URLの書き換えによるセッション)を絶対に利用してはならない、とは言いませんが特別な理由がない限り有効にしてはなりません。ページやURLをメールなどで送信するとセッションIDが漏洩します。
最後にセッションIDは少なくとも定期的にsession_regenerate_id()で更新すべきです。ログインした時の更新は必須
sessionの更新
セッションはサーバー側で管理されるデータであり、セッションの更新は通常、新しいデータで既存のセッションデータを上書きする操作。
このため、セッションの更新が行われると、以前のセッションデータは置き換えられて失われる場合がある。
セッションの更新は、セッションに関連する情報の変更や追加、削除などが行われた場合に発生することがあります。 具体的には、セッションに新しいデータが追加されたり、一部のデータが削除されたりすることがあります。セッションの更新が行われると、更新後のセッションデータが有効になり、以前のデータは失われます。
したがって、セッションの更新が行われる場合には、セッションに含まれていた以前の値が消える可能性があることに注意してください。 セッションの更新が必要な場合には、適切なデータを提供してセッションを更新する必要があります。
SPAでは?
シングルページアプリケーションの場合では以下の変更があっただけ
セッションIDとクッキーが
↓
トークンとAuthヘッダーに代わった
上記の変更で暗号化されたIDから情報を取り出して確認するという基本的な考え方は同じ。