N2
NanToo
Cookie はなぜ壊れやすいのか ― SameSite / Secure / HttpOnly / Partitioned を正しく読む
DDEVELOPER
開発13 分で読める

Cookie はなぜ壊れやすいのか ― SameSite / Secure / HttpOnly / Partitioned を正しく読む

Cookie は、Webでログイン状態を保つための基本技術です。しかし、SecureHttpOnlySameSitePartitioned__Host- などの属性を「とりあえず全部付ければ安全」と覚えると、すぐに判断を間違えます。

Cookie の属性は、それぞれ守る相手が違います。通信路を守るもの、JavaScriptからの読み取りを防ぐもの、クロスサイト送信を制限するもの、第三者コンテキストで状態を分離するもの。本記事では、Set-Cookie の主要属性を、CSRF、XSS、第三者Cookie、CHIPS の文脈で読み直します。

#Cookie#SameSite#HttpOnly#Secure#CHIPS#Webセキュリティ

CookieはステートレスなHTTPに状態を持ち込む

HTTP は基本的にステートレスです。1回目のリクエストと2回目のリクエストを、プロトコルそのものは自動では結びつけません。Cookie はこの世界に「同じブラウザから来た続きのリクエストらしい」という状態を持ち込む仕組みです。

HTTP/1.1 200 OK
Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax

GET /account HTTP/1.1
Cookie: session=abc123

便利な一方で、Cookie は自動送信されます。ブラウザは条件に合うCookieを、ユーザーが明示的に選ばなくてもリクエストに付けます。この「自動で付く」性質が、ログイン維持を便利にする同じ理由で、CSRFやセッション盗難のリスクにもつながります。

SecureはHTTPS限定で送るための属性

Secure は、Cookieを安全な通信路でだけ送るための属性です。MDNの説明では、Secure が付いたCookieは通常 https: のリクエストでのみ送信されます。ログインセッションのCookieには基本的に必須と考えてよい属性です。

Set-Cookie: session=abc123; Secure

ただし、Secure はCookieの中身をアプリ内で安全にする魔法ではありません。ディスク上の保存、ブラウザ拡張、XSSで発生するリクエスト、サーバー側のセッション管理ミスまでは解決しません。また、JavaScriptからの読み取りを止める属性でもありません。そこは HttpOnly の役割です。

HttpOnlyはdocument.cookieから守る

HttpOnly は、CookieをJavaScriptの document.cookie などから読めないようにする属性です。XSSが起きたときに、攻撃者のスクリプトがセッションIDを直接盗み出すリスクを下げます。

Set-Cookie: session=abc123; Secure; HttpOnly

ここで誤解しやすいのは、HttpOnly が付いていても、そのCookieはHTTPリクエストには送られるという点です。MDNも、XMLHttpRequestfetch() によるリクエストでもCookieは送信されると説明しています。つまり、HttpOnly は「読み取り」を防ぐ属性であり、「そのCookieを使ったリクエスト」を止める属性ではありません。

したがって、XSS対策としては HttpOnly だけでは足りません。テンプレートエスケープ、CSP、入力検証、危険なHTML挿入の回避が必要です。Cookie属性は防御の一層であって、XSSそのものを消すわけではありません。

SameSiteはCSRFを減らすが、認可ではない

SameSite は、クロスサイトのリクエストにCookieを送るかどうかを制御します。CSRFは、攻撃者のサイトが被害者のブラウザに、ログイン済みサイトへのリクエストを送らせる攻撃です。OWASPも、ブラウザがセッションCookieを自動的に含めることがCSRFの前提になると説明しています。

大まかな挙動用途の目安
Strict同一siteからのリクエストに限定管理画面、重要操作
Lax同一site + 一部のトップレベル遷移通常のログインセッション
Noneクロスサイトにも送る。Secure 必須埋め込み、外部IdP、決済など

注意点は、SameSite は認可ではないことです。攻撃リクエストの一部をブラウザ側で抑制できますが、サーバー側のCSRFトークン、Origin / Referer確認、Fetch Metadataヘッダー、再認証などを完全に置き換えるものではありません。

siteとoriginを混同しない

SameSite の「site」は、JavaScriptの同一オリジンポリシーでいう origin と同じではありません。origin は scheme、host、port の組み合わせです。一方、SameSiteで見る site は、現在の仕様やブラウザ説明では scheme と登録可能ドメインを中心に扱われます。

組み合わせoriginsite
https://app.example.comhttps://api.example.com別origin同一siteになり得る
https://example.comhttp://example.com別originschemeを含めると別site

この違いを混同すると、「CORSで別originだからSameSiteでも別扱いだろう」といった誤解が起きます。Cookieの送信条件、CORSでレスポンスを読める条件、JavaScriptがDOMへアクセスできる条件は、それぞれ別のルールです。

__Host-と__Secure-はスコープのミスを減らす

Cookie名のプレフィックスも重要です。MDNは、__Secure-__Host- などのCookieプレフィックスを説明しています。これらは、対応ブラウザでCookieの属性条件を強制し、設定ミスを見つけやすくします。

プレフィックス主な条件狙い
__Secure-Secure が必要HTTPS経由のCookieであることを強制
__Host-SecurePath=/Domainなしサブドメインからの上書きや広すぎるDomainを避ける

ログインセッションCookieでは、可能なら __Host- を検討する価値があります。Domain を指定しないhost-only cookieにし、Path=/ でホスト全体に限定すると、スコープが読みやすくなります。

Partitioned CookieとCHIPS

Partitioned は、CHIPS(Cookies Having Independent Partitioned State)で導入された属性です。Chrome Privacy Sandboxの説明では、Partitioned Cookieはトップレベルsiteごとに別のCookie jarへ保存されます。

Set-Cookie: __Host-chat=value; Secure; Path=/; SameSite=None; Partitioned

たとえば support.chat.exampleretail.example に埋め込まれたときに作ったCookieは、retail.example というトップレベルsiteの区画に紐づきます。同じ support.chat.example が別の news.example に埋め込まれても、同じCookieは共有されません。

これは、埋め込みチャット、地図、決済、CDNなど、第三者コンテキストでも状態が必要だが、サイト横断の追跡には使わせたくない場面のための仕組みです。CHIPSは「第三者Cookieを今まで通り使い続ける」技術ではなく、トップレベルsiteごとに状態を分けるための移行先です。

ローカルストレージなら安全、ではない

Cookieの属性が難しいため、「トークンをCookieではなくlocalStorageに置けばよい」と言われることがあります。しかし、これは単純化しすぎです。localStorageの値はJavaScriptから読めるため、XSSが起きるとアクセストークンを直接盗まれる可能性があります。

一方、HttpOnly CookieはJavaScriptから直接読めません。ただし、リクエストには自動送信されるため、CSRFやXSSによる操作リクエストには別の防御が必要です。つまり、CookieとlocalStorageは「どちらが常に安全」ではなく、攻撃面が違います。

セッション管理では、短命トークン、ローテーション、CSRF対策、再認証、権限チェック、ログアウト時の無効化、サーバー側セッション失効などを組み合わせる必要があります。保存場所だけで安全性を決めるのは危険です。

実務でのSet-Cookie例

通常のログインセッションなら、まずは次のような形が出発点になります。サイトの要件によって SameSite=Strict に寄せるか、外部IdPや決済との連携で None が必要かを判断します。

Set-Cookie: __Host-session=...; Path=/; Secure; HttpOnly; SameSite=Lax

第三者コンテキストに埋め込まれるウィジェットで、トップレベルsiteごとに状態を分けたい場合は、CHIPSの Partitioned を検討します。

Set-Cookie: __Host-widget=...; Path=/; Secure; SameSite=None; Partitioned

ただし、これはあくまで形の例です。実際にはブラウザ対応、第三者Cookieブロック、CORS、iframeの権限、Storage Access API、ログイン方式、認可モデルまで含めて確認します。

まとめ ― 属性は役割で読む

Cookie属性は、名前だけ覚えるより「何から守るのか」で読むと整理できます。Secure は通信路、HttpOnly はJavaScriptからの読み取り、SameSite はクロスサイト送信、Partitioned は第三者コンテキストでの状態分離、__Host- はスコープ設定ミスの削減です。

逆に言えば、どの属性も万能ではありません。HttpOnly はCSRFを止めません。SameSite は認可を代替しません。Secure はXSSを防ぎません。Partitioned はすべての第三者Cookie問題を解決するわけではありません。

Cookieは壊れやすい技術ではありますが、壊れ方にはパターンがあります。属性を役割で読み、サーバー側の認可・CSRF対策・XSS対策と組み合わせれば、セッション管理の見通しはかなりよくなります。

参考文献・ソース

記事作成に関する注記

本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。

🔧 関連ツール

📚 関連記事