コンテンツにスキップ

Cookieを完全に理解したい

はじめに

エンジニアとかをやっていると Cookie という名前は聞いたことがあると思います。 「なんか保存しているもの」とかぐらいのイメージの人が多いのではないでしょうか。

この記事では「なぜ Cookie が必要なのか」から順番に整理して、EC サイト・ログイン・広告という 3 つの場面で Cookie がどう使われているかを見ていきます。

Web の通信とステートレス

私たちは日頃から Web サイトに対して何かをして、その結果をうけて内容を表示します

ここの通信のやり取りは、HTTPという通信プロトコルを利用して実現しています しかし、HTTPはステートレスであるという特徴を持つので状態をもちません

:::message HTTP はステートレス(状態を持たない) 1往復が終わると、サーバーはそのブラウザのことをすぐ忘れます。次のリクエストが来ても「初対面のお客さん」として扱います :::

Cookie はこのステートレスな HTTP において、ステートフルなやりとりを実現する仕組みです。 サーバーがブラウザに「メモ」を渡し、ブラウザが毎回そのメモを持参することで、サーバーは「前回と同じ相手」を把握できます。

我々がよく利用する Web サービスの多くは、事実上ステートフルです。 ログイン状態の維持、カートの保持、設定の記憶など、「前回の続き」がなければ快適に使えないものばかりです。 それを実現しているのが Cookie という仕組みです。

具体的にいうと以下のような場面で利用されています。

  • ログイン機能があるWebサイト
  • EC サイトなどのカートの情報
  • 広告

Cookie とは「サーバーがブラウザに渡す小さなテキストデータ」です。

まずサーバーが Set-Cookie というヘッダーでブラウザに「これ覚えておいて」と渡します。 ブラウザはそのデータを保存しておいて、次に同じサーバーへリクエストする時に、リクエストヘッダーに Cookie を自動で追加して送ります。

sequenceDiagram
  actor U as ユーザー
  participant B as ブラウザ
  participant S as サーバー

  U->>B: サイトを開く
  B->>S: GET /(初回リクエスト)
  S-->>B: 200 OK + Set-Cookie: cart_id=xyz123
  Note over B: cart_id=xyz123 を保存

  U->>B: 別のページを開く
  B->>S: GET /cart + Cookie: cart_id=xyz123
  Note over B: 自動で Cookie を付けて送る
  S-->>B: 200 OK(同じブラウザと識別できた)

ポイントは 2 つです。

  1. 自動で送る:ユーザーが何か操作するわけではなく、ブラウザが自動でやってくれます
  2. サーバーが発行する:ブラウザが勝手に作るわけではなく、必ずサーバーから Set-Cookie で渡されます

ブラウザに Cookie の情報が保存されるので、ブラウザの変更(ChromeからSafariなど)やシークレットモードでは Cookie が引き継がれず、ログイン状態なども保持されません。

Cookie が実際にどう使われているか、3 つの場面で見ていきます。

  1. EC サイト — 未ログインでもカートを引ける
  2. ログイン — Session ID を運んで認証状態をつなぐ
  3. 広告 — 閲覧履歴を文脈として使う

どれも発想は同じで、前回の手がかりを次回のリクエストに載せることが共通しています。

ログインしていなくても、Cookie でそのブラウザのカートを識別できます。

  1. 初回訪問:サーバーがカート識別子を発行(Set-Cookie: cart_id=xyz
  2. 商品を追加:ブラウザが cart_id を自動送信(Cookie: cart_id=xyz
  3. 後日アクセス:同じ ID から前回のカートを復元(未ログインでも「続き」が見える)

:::message Cookie に入っているのは「カートの中身そのもの」ではなく、カートを引くための ID であることがポイントです。実際の商品一覧や数量はサーバー側で管理されます。 :::

属性 役割
Cookie 名と値 cart_id=xyz カートを引く識別子
有効期限 Max-Age=2592000 ブラウザを閉じた後もカートを残す
HTTPS 限定 Secure HTTPS 通信だけに限定する
JS からの読取制限 HttpOnly XSS 経由の露出を減らす

Cookie 単体でも theme=dark(表示設定)や lang=ja(言語設定)のような小さな値は持てます。

ただし、ユーザーの認証状態や詳細なカートの中身まで Cookie に全部入れるのは危険です。そこで登場するのが Session ID との組み合わせです。Cookie には短い ID だけ入れて、その ID に紐づくデータはサーバー側で管理します。Cookie は「鍵」を運び、「金庫の中身」はサーバーが持つイメージです。

ログインの主役は認証とセッション管理です。Cookie はその結果として発行された Session ID を運ぶ役を担います。

仕組み 役割
Cookie 「このブラウザは誰の続きか」の手がかりを送る
セッション ログイン中か、有効期限内かをサーバーが判定する

ログイン後に別ページへ移動しても本人だと分かるのは、Cookie が Session ID を毎回自動で送り返しているからです

Session ID はどうやって検証するのか

Session ID は crypto.randomUUID() などで生成した推測不可能な文字列です。 サーバー側では Redis やデータベースに session_id → ユーザー情報 のマッピングを保存しておき、リクエストのたびに照合します。

セッションストア(Redis / DB)

session_id=abc → { user_id: 42, role: member, expires: 2026-04-03 11:00 }
  • 存在しない ID が来た場合 → ログアウト状態として扱う(401 を返すなど)
  • 有効期限が切れた ID が来た場合 → 同様にログアウト扱い
  • 一定時間操作がないとタイムアウトして自動ログアウトするのもこの仕組みです

ここにログイン後の DevTools > Application > Cookies で session_id が保存されているスクリーンショットを置いて

属性 役割
Cookie 名と値 session_id=abc 認証済みセッションを引くための鍵
JS からの読取制限 HttpOnly XSS 経由で Session ID が盗まれにくくなる
HTTPS 限定 Secure HTTPS のときだけ送る
クロスサイト制御 SameSite=Lax 別サイトからの POST に session_id が送られない。CSRF 対策として機能する

:::message なぜ SameSite が CSRF 対策になるのか

session_id を Cookie で管理していると、ブラウザは利用者の意思とは別にその値を自動で送ります。つまり、利用者がログインしたまま悪意のある外部サイトを開き、そこで意図しない POST が発生すると、ブラウザは session_id を付け、サーバーはそれを「正しいログイン済みユーザーからの操作」と誤認しうるわけです。これが CSRF の基本的な危険です。

厳密には、CSRF は session_id 自体を盗まなくても成立します。session_id を script などで盗まれるのは XSS やセッションハイジャックの話で、そちらには HttpOnly が重要です。一方の CSRF では、ログイン中ブラウザが外部サイト経由のリクエストにも Cookie を自動で添えてしまうことが本質です。SameSite=Lax は、そうした別サイト起点の POSTsession_id を送りにくくすることで、攻撃を成立しにくくします。

IPA の 安全なウェブサイトの作り方 - 1.6 CSRF(クロスサイト・リクエスト・フォージェリ) でも、ログインした利用者からのリクエストが「その利用者が意図したものか」を識別できないと問題になると整理されています。 :::

ログアウトの流れ

sequenceDiagram
    participant B as ブラウザ
    participant S as サーバー
    B->>S: POST /logout
    Note over S: セッションを破棄(DBから削除)
    S-->>B: レスポンス + Max-Age=0 でCookie削除を指示
    Note over B: Cookie を削除

    Note over B,S: ログアウト後に再アクセス
    B->>S: GET /mypage(Cookie なし)
    S-->>B: 401 / ログインページへリダイレクト
    Note over S: session_id がないため未認証として扱う

ログアウトのポイントは「2つ同時にやらないといけない」という点です サーバー側でセッションを破棄するだけではブラウザにはまだ session_id が残っています ブラウザの Cookie を消すためには、サーバーから Max-Age=0 を返して削除を指示する必要があります

:::message alert ログアウトには、サーバー側のセッション破棄ブラウザ側の Cookie 削除の両方が必要です。どちらか片方だけでは不完全なログアウトになります。 :::

日常でよくある「さっき見ていた商品が別サイトでも広告に出る」という体験。これも Cookie で実現しています。

Cookie には発行元によって 2 種類があります。

  • ファーストパーティ Cookie:今見ているサイト自身(同じドメイン)が発行する Cookie。EC サイトのカートやログインがこれです。
  • サードパーティ Cookie:今見ているサイトとは別のドメインが発行する Cookie。ページに埋め込まれた広告ネットワークなどが発行します。

サードパーティ Cookie の特徴は、ユーザーが直接そのサービスを訪問していなくても発行・送信される点です。これがサイトをまたいだ追跡を可能にします。

sequenceDiagram
    participant B as ブラウザ
    participant A as サイトA
    participant Ad as ad-network
    B->>A: 訪問
    A-->>B: ページ(広告枠あり)
    B->>Ad: 広告の読み込みリクエスト
    Ad-->>B: 広告 + Set-Cookie: user=xxx
    Note over B: ad-network の Cookie を保存

サイト A を見たとき、ページに広告枠が埋め込まれています。ブラウザは ad-network にも広告を取りに行くため、ad-network が Set-Cookieuser=xxx を発行します。ここが重要なポイントで、ユーザーは ad-network のサイトを自分で訪問したわけではないのに Cookie が保存されています。

仕組み:サイト B での追跡

sequenceDiagram
    participant B as ブラウザ
    participant Ad as ad-network
    participant SB as サイトB
    B->>SB: 訪問
    SB-->>B: ページ(広告枠あり)
    B->>Ad: 広告の読み込みリクエスト + Cookie: user=xxx
    Note over Ad: サイトAもBも見た人と判明
    Ad-->>B: サイトAで見た商品の広告

次にサイト B を訪問すると、そこにも同じ ad-network の広告枠があります。ブラウザは ad-network に広告を取りに行くとき、サイト A で保存した user=xxx を自動で送ります。ad-network は「この人はサイト A もサイト B も見た人だ」と分かるので、サイト A で見た商品の広告を返せます。これが「さっき見た商品が別サイトでも出る」の正体です。

属性 役割
Cookie 名と値 ad_id=xyz ブラウザを継続して識別するための ID
クロスサイト制御 SameSite=None クロスサイト文脈でも送るために必要
HTTPS 限定 Secure SameSite=None とセットで必要
有効期限 Max-Age=2592000 日をまたいで同じ ID を使いやすくする

EC サイトやログインでは SameSite=Lax / Strict で他サイトからの送信を制限しますが、広告はサイトをまたいで送る必要があるので SameSite=None にします。ただし SameSite=NoneSecure とセットでないと使えません。

この仕組み自体は攻撃ではなく広告配信の技術です。ただ、ユーザーが知らないうちにサイトをまたいで行動を追跡されることへの問題意識が高まり、ブラウザ各社が規制を強化しています。

ブラウザ 対応
Safari 2020年〜 全面ブロック(ITP)
Firefox 2023年〜 デフォルトブロック(ETP)
Chrome ユーザー選択制へ移行

参考 - Full Third-Party Cookie Blocking and More — WebKit Blog - Enhanced Tracking Protection in Firefox for desktop — Mozilla Support - Third-party cookies | Privacy Sandbox — Google

まとめ

  1. Cookie は、サーバーが Set-Cookie でブラウザに保存させる小さなテキストデータ
  2. ブラウザは次回以降、対応するリクエストでその Cookie を自動送信する
  3. Cookie 単体で設定や小さな手がかりを持てるし、Session ID と組み合わせればサーバー側の状態も引ける
  4. EC の未ログインカート、ログイン維持、広告はすべてこの発想の延長線上にある
  5. ログアウトではセッション破棄と Cookie 削除の両方が必要

Cookie の本質はひとつ、「サーバーがブラウザに渡すメモ」です。ブラウザはそのメモを自動で持参するので、ステートレスな HTTP でも「前回の続き」が実現できます。