Cookie とセッション — 状態を持たない HTTP に記憶を持たせる
サーバーが覚えるか、利用者に持たせるか。この選択が、ログアウトの実装可能性とスケール特性をすべて決める。
先に読んでおくとよい記事
この記事の進み方
What — セッションはどこに置かれているか
HTTP は状態を持ちません。1回目のリクエストと2回目のリクエストは、サーバーから見れば無関係な他人です。「ログイン中」という状態を成立させるには、毎回何かを持たせる必要があります。
Cookie に入っているのはセッション ID だけで、中身はサーバーが持つ。だからサーバー側で1行消せば、その瞬間にログアウトが成立する。
Cookie の属性は「既定が危険」
- HttpOnly
- JavaScript から読めなくする。XSS が起きても Cookie を盗み出せない。認証用 Cookie には必須。
- Secure
- HTTPS の通信でしか送らない。付けないと、平文通信に紛れ込んだ瞬間に盗聴される。
- SameSite=Strict
- 他サイトからの遷移では一切送らない。安全だが、外部リンクから来るとログアウト状態に見える。
- SameSite=Lax
- 他サイトからのリンク遷移(GET)では送るが、POST では送らない。現在の多くのブラウザの既定値。
- SameSite=None
- 他サイトからでも送る。Secure が必須。埋め込みウィジェットなど、明確な理由があるときだけ。
- Domain / Path
- 送信先の範囲。Domain を広げるとサブドメイン全体に送られる。広げすぎると、1つのサブドメインの脆弱性が全体に波及する。
- Max-Age / Expires
- 有効期限。指定しないとブラウザを閉じると消える(セッション Cookie)。
Set-Cookie: sid=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
この4つの属性を書かない理由は、基本的にありません。
サーバーが覚えるか、利用者に持たせるか
ID だけを渡し、中身はサーバーが持つ。サーバーが状態を持つ。
Cookie: sid=9f2a...
サーバー側:
9f2a... → { userId: 42, role: "admin", ... }- 即時ログアウト
- できる
- 権限変更の反映
- 即座に効く
- 毎リクエストの参照
- 必要
- サーバーの状態
- 持つ(共有が必要)
- Cookie のサイズ
- 数十バイト
- サイズ
- —
- –サーバーを複数台にすると、セッションを共有する仕組み(Redis など)が要る。
- –退会・アカウント停止・権限剥奪をその場で反映できるのが最大の利点。
どちらが優れているかではなく、即時失効が必要か、状態を持たせたくないかで決まる。多くのサービスでは、この2つを組み合わせる。
Why — なぜ Cookie は自動で送られるのか
Cookie が発明されたのは1994年、ネットショッピングのカートを実現するためでした。「前のページで何を入れたか」をサーバーが思い出せないと、カートが成立しません。
このとき選ばれたのが「ブラウザが勝手に付けて送る」という設計です。ページを書く人が毎回コードを書く必要がなく、フォーム遷移でもリンク遷移でも画像の読み込みでも、自動的に状態が引き継がれます。
この自動性が、利便性と脆弱性の両方を生みました。
- 便利: ログイン状態がタブをまたいでも維持される
- 危険: 攻撃者のサイトからのリクエストにも、自動で付いてしまう
後者が CSRF です。攻撃者のページに <form action="https://bank.example.com/transfer" method="POST"> を置いて自動送信すれば、ブラウザは律儀にあなたの Cookie を付けて送ります。
SameSite 属性は、この自動送信を「どこから来たリクエストか」で制限するために、20年以上経ってから追加された対策です。
SameSite=Lax だけで CSRF は防げるか
現在の多くのブラウザは SameSite の既定を Lax にしました。これにより、他サイトからの POST では Cookie が送られません。CSRF の大部分はこれで塞がります。
ただし「防げた」と言い切れない理由が3つあります。
- GET で状態を変えている場合は素通り。
Laxはリンク遷移(GET)では Cookie を送ります。GET /delete?id=1のような設計だと防げません。 - 古いブラウザや一部の環境では既定が異なる。 明示的に指定しない限り、挙動は保証されません。
- 同一サイト内からの攻撃には無力。 XSS があれば、そのページ自身からリクエストできるので
SameSiteは関係ありません。
サーバーが覚える方式(セッション)と、利用者に持たせる方式(トークン)。ログアウトを即座に効かせやすいのはどちらですか?
演習 — まず自分で判断する
解説を読む前に、まず自分で判断してみてください。ここで一度詰まっておくと、 次の節の判断軸が「なるほど」ではなく「そう来たか」に変わります。
この要件で、認証の保持方式と Cookie の属性をどう設計しますか?
- BtoC のサービス。Web とスマホアプリの両方から使う
- Web は https://app.example.com、API は https://api.example.com(別オリジン)
- 不正利用が疑われるアカウントを、運営が即座に強制ログアウトできる必要がある
- ユーザーは決済情報を登録するため、権限変更は即時反映したい
- ログイン状態は2週間維持したい(毎回ログインさせたくない)
- 外部サイトからの流入が多く、リンクを踏んで来たときにログイン状態が見えていてほしい
- · 即時強制ログアウトという要件が、方式の選択にどう効くか
- · 別オリジン構成で Cookie を使うとき、どの属性が必要になるか
- · 「2週間維持」と「即時失効」は両立するか
「セッション Cookie の属性が付いていない」という診断結果を、開発チームに説明してください
- 相手は動いているので問題ないと考えている
- 社内には HTTP のままの内部ツールがある
- 3つの属性それぞれの必要性を納得してもらいたい
読み終わりましたか?
読了にすると、これを前提とする記事がロードマップで開放されます。