セッション
ログイン中のユーザーの状態をサーバ側で保持する仕組み。Cookieと対で機能する。
概要
セッションは、ユーザーがログインしてからログアウトするまでの「つながり」をサーバ側で管理する仕組みです。ログインに成功すると、サーバは「セッションID」と呼ばれるランダムで推測困難な文字列を発行し、Cookieとしてブラウザに渡します。ユーザーが誰で、いつログインして、カートに何を入れたか——そうした実際の状態はサーバ側に保管し、ブラウザには「引換券」であるIDだけを持たせるのがポイントです。
ECサイトの買い物カゴ、管理画面のログイン状態、フォームの入力途中の値など、「ページをまたいで覚えていてほしい情報」のほとんどはセッションの上に載っています。Webアプリの認証を実装するとき、最初に向き合うことになる語彙です。
なぜ生まれたか
HTTPはステートレスなプロトコルとして設計されました。リクエストとレスポンスの一往復が終わるたびに関係は切れ、サーバは「さっきの人がまた来た」ということを覚えていません。文書を配るだけの初期のWebではそれで十分でしたが、ログインや買い物カゴのような「状態を持つアプリケーション」を作ろうとした途端、この健忘症が問題になりました。
毎回IDとパスワードを送らせるのは危険かつ不便です。かといって、ユーザー情報のすべてをブラウザ側に持たせると、改ざんし放題になってしまいます。そこで「本体はサーバが預かり、ブラウザには番号札だけ渡す」という分担が考え出されました。番号札の受け渡し手段としてCookieがちょうど同じ時期(1990年代半ばのNetscape)に発明されたことで、セッション方式はWebの標準的なパターンとして定着しました。
詳細
発行と照合の往復
セッションの一生は「ログイン時の発行」と「以降のリクエストごとの照合」の2つの局面でできています。
ブラウザは同じサイトへのリクエストにCookieを自動で添付するため、ユーザーは何も意識しません。サーバは受け取ったIDをセッションストアで引き、対応するデータが見つかれば「ログイン済みの本人」として扱います。ログアウトはストア側のデータを破棄するだけで済み、番号札は即座に無効になります。この「サーバ側でいつでも失効できる」性質がセッション方式の大きな強みです。
セッションストアをどこに置くか
状態の実体を保存する場所(セッションストア)の選択は、地味に見えて重要な設計判断です。最も手軽なのはアプリケーションプロセスのメモリ内ですが、サーバを再起動すると全員がログアウトし、何よりサーバを複数台に増やした瞬間に破綻します。ロードバランサがリクエストを別のサーバに振り分けると、そこにはセッションデータが存在しないからです。
対策は大きく2つあります。ひとつは同じユーザーを常に同じサーバへ送る「スティッキーセッション」ですが、サーバの追加・削除がしにくくなりスケーリングの柔軟性を損ないます。もうひとつは、RedisのようなインメモリのキャッシュストアやRDBMSにセッションを外出しし、どのサーバからも同じストアを参照する構成です。実務ではこちらが主流で、「状態をアプリサーバから引き剥がす」というスケールアウトの定石の典型例になっています。
ステートレスなトークンとの対比
セッションの対極にあるのが、JWTに代表される「サーバに状態を持たない」方式です。ユーザー情報を署名付きトークンに焼き込んでブラウザに渡してしまえば、サーバはストアを引かずに検証だけで本人確認ができます。ストアが不要になる代わりに、「発行済みトークンを即座に無効化しにくい」という弱点を抱えます。セッションは照合のたびにストアへのアクセスが走るが失効は自在、JWTは検証が軽いが失効が難しい——この対称関係を押さえておくと、認証まわりのアーキテクチャ議論が一気に読みやすくなります。
セキュリティ上の落とし穴
セッションIDは「持っているだけで本人になれる」鍵なので、盗まれれば乗っ取り(セッションハイジャック)が成立します。通信経路での盗聴はHTTPSで防ぎ、XSSによるスクリプトからの窃取はCookieの HttpOnly 属性で防ぎます。また、ブラウザがCookieを自動送信する性質そのものを悪用するのがCSRFで、SameSite 属性やトークンによる対策が必要です。もうひとつ忘れがちなのが「ログイン成功時にセッションIDを必ず振り直す」こと。これを怠ると、攻撃者が事前に仕込んだIDでログインさせるセッション固定攻撃が成立します。