クッキーは、Webにおいて最も頻繁に誤設定されるセキュリティコントロールの1つです。たった1つの属性が欠けているだけで、セッションハイジャック、CSRF、クロスサイト情報漏洩のリスクが生じます。モダンブラウザはデフォルト設定を厳格化していますが、各属性を理解し適切に組み合わせて多層防御を実現することが不可欠です。
主要なセキュリティ属性はSecure、HttpOnly、SameSite、そして__Host- / __Secure-プレフィックスです。それぞれ異なる目的を持ち、組み合わせることで最大の効果を発揮します。
SameSite属性
SameSiteはクロスサイトリクエスト時のクッキー送信を制御し、CSRF攻撃に対する主要な防御策です。
Set-Cookie: session=abc123; SameSite=Lax
| 値 | 動作 | 使用例 |
|---|---|---|
Strict | 同一サイトリクエストのみ送信 | CSRFトークン、機密操作 |
Lax(デフォルト) | トップレベルGETナビゲーションのみ送信、クロスサイトPOST・画像・スクリプト・iframeでは送信しない | 一般的なセッションクッキー |
None | 全クロスサイトリクエストで送信、Secure必須 | サードパーティウィジェット、埋め込みコンテンツ、API連携 |
モダンブラウザではSameSite=Laxがデフォルトであり、ほとんどのセッションクッキーに適しています。CSRFトークンや重要な操作にはStrictを使用します。Noneはサードパーティの埋め込みが本当に必要な場合のみ使用し、必ずSecureと組み合わせてください。
Secureフラグ
Secure属性はHTTPS経路でのみクッキーを送信することを保証します。これがないと、暗号化されていない接続(特に公衆Wi-Fi)でクッキーが傍受される可能性があります。
Set-Cookie: session=abc123; Secure
このフラグはhttp://オリジンからは設定できません。HTTPSページのコンテキストが必要です。SameSite=Noneを使用する場合、Secureは必須です。ブラウザはSecureなしのSameSite=Noneクッキーを拒否します。
HttpOnly属性
HttpOnlyはJavaScriptからのdocument.cookieアクセスを防止します。XSS脆弱性があった場合でも、攻撃者はセッションクッキーを直接読み取ることができなくなります。
Set-Cookie: session=abc123; HttpOnly
トレードオフとして、クライアントサイドのJavaScriptはHttpOnlyクッキーを読み書きできません。SPAの認証フローでクッキーを検査する必要がある場合は、認証状態を確認する専用のAPIエンドポイントを使用するか、XSSリスクのあるコンテキストではインメモリストレージによるトークンベース認証を検討してください。
PathとDomainスコーピング
Path属性はクッキーを特定のURLパスに制限します。Domain属性はどのサブドメインがクッキーを受信するかを制御します。
Set-Cookie: app_session=xyz; Path=/app; Domain=.example.com
Path=/はサイト全体、/adminのように特定のパスに制限することもできます。Domain属性は広く設定しすぎると危険です。Domain=.example.comはstaging.example.comやdev.example.comを含む全サブドメインからアクセス可能になり、これらのサブドメインが侵害された場合にリスクが生じます。サブドメイン間の共有が明確に必要な場合のみ、広いDomainを設定してください。
クッキープレフィックス
クッキープレフィックスは、クライアントサイドコードでは迂回できないプロトコルレベルの保証を提供します。
Set-Cookie: __Host-session=abc123; Secure; Path=/
Set-Cookie: __Secure-csrf=xyz; Secure
| プレフィックス | 要件 |
|---|---|
__Host- | Secure必須、Path=/必須、Domain属性なし — オリジンに制限 |
__Secure- | Secure必須 — ドメインスコープでSecureを保証 |
セッショントークンには__Host-sessionを使用します。このプレフィックスによりクッキーが単一オリジンに制限され、サブドメイン経由の攻撃を防止します。ブラウザがプロトコルレベルで要件を強制するため、JavaScriptでプレフィックスを取り除くことはできません。
サイズ制限とCHIPS(Partitioned Cookies)
ブラウザはクッキーに制限を設けています:約4KB/クッキー、ドメインあたり50-150個、オリジンあたり10-50個。データを直接クッキー値に埋め込むのではなく、セッションIDなどの参照のみを保存することで制限内に収めます。
CHIPS(Cookies Having Independent Partitioned State)はPartitioned属性を導入します。トップレベルサイトごとに独立したクッキージャーを作成し、クロスサイトトラッキングを防止しつつ、埋め込みサードパーティサービスが状態を維持できるようにします。
Set-Cookie: widget_pref=dark; Secure; SameSite=None; Partitioned; Path=/
ブラウザサポート:Chrome 114+、Edge 114+、Opera。決済iframe、埋め込みマップ、サードパーティチャットウィジェットなど、クロスサイトトラッキングを有効にせずに状態維持が必要なケースに最適です。
クロスサイトとクロスオリジンの動作
same-siteとsame-originは異なる概念であり、クッキー送信に影響します。https://app.example.comとhttps://api.example.comはsame-site(同一登録ドメイン)ですがcross-origin(異なるホスト名)です。SameSiteルールはオリジンではなくサイト単位で評価されます。
クロスオリジンフェッチでクッキーを送信するには:
fetch("https://api.example.com/data", {
credentials: "include",
});
サーバー側でAccess-Control-Allow-Credentials: trueの応答が必要です。SafariのITPとFirefoxのETPは、特にユーザーインタラクションのないクッキーに対して、有効期間やクロスサイトアクセスに追加の制限を課します。
実践的設定レシピ
// Express.js
app.use(session({
name: "__Host-session",
secret: process.env.SESSION_SECRET,
cookie: {
secure: true,
httpOnly: true,
sameSite: "lax",
path: "/",
},
}));
// Next.js APIルート
export async function GET(request) {
return new Response(JSON.stringify({ ok: true }), {
headers: {
"Set-Cookie":
"session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/",
},
});
}
| クッキーの種類 | 設定 |
|---|---|
| 認証セッション | HttpOnly; Secure; SameSite=Lax; Path=/ |
| CSRFトークン | __Host-csrf=xyz; Secure; SameSite=Strict; Path=/ |
| サードパーティウィジェット | Secure; SameSite=None; Partitioned; Path=/ |
テストとデバッグ
ブラウザDevToolsのApplicationタブでは、すべてのクッキーとその属性、SecureやHttpOnlyの状態を確認できます。コマンドラインでのテスト:
# クッキーの動作確認
curl -v -c cookies.txt -b cookies.txt https://example.com/login
ネットワークタブのレスポンスヘッダーでSet-Cookieを確認し、期待する属性がすべて含まれていることをチェックします。CIパイプラインにOWASP ZAPなどの自動クッキースキャナーを組み込み、定期的なセキュリティ監査を実施しましょう。
まとめ
多層防御のクッキー戦略では、利用可能なすべての属性を適切に組み合わせます。多くのセッションクッキーにはHttpOnly + Secure + SameSite=Laxがデフォルトの推奨設定です。CSRFトークンには__Host-プレフィックスとSameSite=Strictを追加します。SameSite=Noneはサードパーティコンテキストで本当に必要な場合のみ予約します。ブラウザのプライバシーとセキュリティのデフォルトが進化し続ける中、DevToolsと自動スキャナーの両方を使用した定期的な監査で、クッキー設定の安全性を維持してください。
