【JS応用】Cookies(クッキー), document.cookie

概要:現代のフロントエンドにおけるCookieの立ち位置と役割

Web開発の黎明期から存在する「Cookie」は、ステートレスなHTTPプロトコルにおいて、クライアントとサーバー間で状態を維持するための極めて重要な仕組みです。しかし、近年のブラウザのプライバシー保護強化(ITPやサードパーティCookieの制限など)により、その扱いはかつてないほど複雑かつ慎重さが求められるものとなっています。

本記事では、フロントエンドエンジニアが避けては通れない`document.cookie`の仕様から、モダンなWebアプリケーションにおけるCookieの正しい設計思想、そしてセキュリティを担保するためのベストプラクティスまでを網羅的に解説します。単なる「データの保存場所」としてではなく、Webセキュリティの最前線という視点でCookieを再定義していきましょう。

詳細解説:Cookieの構造とdocument.cookieの罠

Cookieは、サーバーから`Set-Cookie`レスポンスヘッダーを通じてブラウザに送信され、以降の同一ドメインへのリクエストに自動的に付与される小さなデータ片です。

1. Cookieの属性が握るセキュリティの鍵

Cookieを扱う上で、以下の属性は「必須」と言っても過言ではありません。これらを理解せずに設定することは、脆弱性を放置することと同義です。

* **Secure**: HTTPS通信でのみCookieを送信します。現代のWeb開発では必須です。
* **HttpOnly**: JavaScriptからのアクセス(`document.cookie`)を禁止します。XSS(クロスサイトスクリプティング)攻撃によるセッションハイジャックを防ぐための最強の盾です。
* **SameSite**: CSRF(クロスサイトリクエストフォージェリ)攻撃を防止します。`Strict`(同サイトのみ)、`Lax`(トップレベルナビゲーションのみ許可)、`None`(制限なし)の3種類がありますが、デフォルトで`Lax`が適用されるのが現代のブラウザ標準です。
* **Expires / Max-Age**: Cookieの有効期限を制御します。指定がない場合は「セッションCookie」となり、ブラウザを閉じると消滅します。

2. document.cookieの特異な仕様

`document.cookie`は、文字列の操作という極めて原始的なインターフェースを提供します。これが多くのバグや混乱を生む要因です。

* **書き込み**: `document.cookie = “key=value; path=/; …”` という形式で代入しますが、これは既存のCookieを上書きするのではなく、新しいCookieを追加する動作になります。
* **読み込み**: すべてのCookieが「キー=値; キー=値; …」という巨大な1つの文字列として返されます。そのため、特定の値を抽出するには、文字列の分割、デコード(`decodeURIComponent`)、トリミングといった処理を毎回記述する必要があります。

このインターフェースは非常に低レイヤーであるため、実務では直接操作するのではなく、適切にラップしたユーティリティ関数を使用するか、ライブラリ(`js-cookie`など)の利用が推奨されます。

サンプルコード:安全かつ効率的なCookieハンドリング

まずは、`document.cookie`を直接触る際の実装例を見てみましょう。可読性と安全性を高めるための実装です。


/**
 * Cookieを安全にセットする関数
 */
function setCookie(name, value, days = 7) {
  const date = new Date();
  date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
  
  const expires = "; expires=" + date.toUTCString();
  const secure = window.location.protocol === 'https:' ? "; Secure" : "";
  
  // SameSite属性の設定(モダンブラウザ対応)
  document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${expires}; path=/; SameSite=Lax${secure}`;
}

/**
 * Cookieをキー指定で取得する関数
 */
function getCookie(name) {
  const nameEQ = encodeURIComponent(name) + "=";
  const ca = document.cookie.split(';');
  
  for (let i = 0; i < ca.length; i++) {
    let c = ca[i].trim();
    if (c.indexOf(nameEQ) === 0) {
      return decodeURIComponent(c.substring(nameEQ.length, c.length));
    }
  }
  return null;
}

/**
 * Cookieを削除する関数
 */
function deleteCookie(name) {
  document.cookie = name + '=; Max-Age=-99999999; path=/;';
}

このコードのポイントは以下の通りです。
1. **エンコードの徹底**: `encodeURIComponent`を使用し、特殊文字によるバグを防いでいます。
2. **Secure属性の動的付与**: 開発環境(HTTP)と本番環境(HTTPS)の両方に対応しつつ、セキュリティを確保しています。
3. **パスの固定**: `path=/`とすることで、アプリケーション全体で一貫したCookie利用を保証しています。

実務アドバイス:フロントエンドエンジニアとしての判断基準

実務においてCookieを設計する際、以下の3つの判断基準を持つことが重要です。

1. そもそもCookieに保存すべきか?

認証トークン(JWTなど)やセッションIDはCookieに保存するのが定石ですが、UIの表示状態(ダークモード設定やモーダルの開閉状態)までCookieに保存するのは避けるべきです。これらは`localStorage`や`sessionStorage`、あるいはグローバル状態管理ライブラリ(Redux, Zustand, Recoil)に任せましょう。Cookieはサーバーに毎回送信されるため、無駄に肥大化するとパフォーマンス低下を招きます。

2. 「HttpOnly」を最優先にする

機密性の高い情報(セッションIDなど)は、絶対にクライアントサイドのJavaScriptから触らせてはいけません。フロントエンドエンジニアとしては「JavaScriptからCookieが読めない」ことを前提とした設計、つまり「サーバーサイドで認証を完結させる」アーキテクチャを目指すべきです。

3. サードパーティCookieの終焉を意識する

Google Chromeをはじめとする各ブラウザは、サードパーティCookieの段階的な廃止を進めています。トラッキングや広告計測のためにCookieを利用している場合、`First-Party Sets`や`Storage Access API`といった代替技術へのキャッチアップが不可欠です。これらを無視した設計は、将来的に機能不全に陥るリスクが高いことを認識してください。

まとめ:Cookieと歩むべき未来

Cookieは、Webの歴史そのものと言っても過言ではないほど、枯れた技術でありながら、現在もなおWebの心臓部で動き続けています。`document.cookie`という少し癖のあるインターフェースと向き合うことは、フロントエンドエンジニアにとって「ブラウザの動作原理」を深く理解するための登竜門です。

本記事で解説した以下のポイントを常に意識してください。
* **セキュリティ属性(Secure, HttpOnly, SameSite)を常に意識する。**
* **不要なデータをCookieに詰め込まない。**
* **Cookieの直接操作は最小限にし、堅牢なユーティリティで抽象化する。**

Webの世界はプライバシー保護の観点から劇的に変化しています。しかし、Cookieという仕組みが持つ「ステートの保持」という本質的なニーズが変わることはありません。フロントエンドスペシャリストとして、Cookieの制約を「制限」ではなく「安全な境界線」と捉え、より堅牢でセキュアなアプリケーションを構築していきましょう。

もしあなたが現在、Cookieの扱いに不安を感じているのであれば、まずは現在のアプリケーションで発行されているCookieをブラウザのデベロッパーツール(Applicationタブ)で確認するところから始めてみてください。そこには、あなたのアプリケーションの設計思想が、生のデータとして刻まれているはずです。

コメント

タイトルとURLをコピーしました