🥜

Cookieの特徴、属性の詳細などを体系的に調べてみた

に公開
2

はじめに

よく使うけれどあまり知らなかった Cookie というものについて、色々ときちんと調べてみた備忘録です。

  • Cookie is 何
  • SessionStorage や LocalStorage とどう違うの
  • 色々と属性を指定できるけれどよく知らないかも
  • サードパーティ Cookie?

上記のような方々に読んでいただけると、もしかしたら少しだけ Cookie の理解が深まるかも?と思いましたので、気になったらぜひ読んでみてもらえたら嬉しいです!

Cookie, SessionStorage, LocalStorage

Cookie はブラウザが備えているデータの入れ物なのですが、まず同じブラウザの入れ物の類として以下が存在します。

  • Cookie
  • SessionStorage
  • LocalStorage

少し似ているのでこれらの違いからさらっとご説明します(後々の属性の説明あたりでも効いてくると思っています)。

項目 Cookie SessionStorage LocalStorage
保存形式 キーバリュー キーバリュー キーバリュー
保存量上限 極小 多め 多め
保存期間 永久保存 タブの間だけ保存 永久保存
分離方法 一部共用可能 そもそも無理 オリジンごとに完全分離
セキュリティの機構 あり なし なし

まずはおおよそ表形式で違いを説明すると、上記の通りです。
保存形式はすべて同じキーバリュー方式であり、保存量に関しては Storage とつく 2 つの方が Cookie よりも多くなります。

保存期間について、SessionStorage はブラウザを閉じるとすべてデータが消えます。短い時間のユーザー体験的に必要なデータを保存する場所と言えそうです。

Cookie と LocalStorage は永久的に保存されるため、一度保存すると利用者の手で削除するのが難しいものになります。数 MB の容量はありますが、開発により不正に残ったりすると面倒なため、取り扱いにはわりと注意が必要です。

分離方法という言葉はとてつもなくわかりにくいのですが、たとえばHoge: Fugaというキーバリューを保存した場合に、別タブ同士でそのキーバリューを共用できるか、という観点です。

前述した通り SessionStorage はタブごとにあるデータストアになるため、そもそも別タブになった時点で、その性質上どう足掻いても共用は無理です。LocalStorage は同じオリジン間であればデータストアの共用は可能ですが、異なるオリジンの場合は完全に分離されます。a.com のブラウザで保存したHoge: Fugaは、b.com のブラウザからHogeキーでリクエストしても、取得することはできません。

Cookie は、条件付きで別のオリジンで発行されたストアにアクセスすることができます。

以下は適当に開いていたFontAwesomeのトップページです。
後ほど紹介しますが、赤線部分が FontAwesome とは直接関係ないサーバーが発行した Cookie のストアデータです。

Domain属性が別のデータ

最後にセキュリティの機構についてですが、こちらも LocalStorage や SessionStorage には特にありませんが、Cookie には存在しますので、後述します。

ご存知の通り、ブラウザとサーバー間でやり取りする際に Set-Cookie ヘッダーがレスポンスヘッダーに返された場合、それ以降のサーバーとの通信においては開発者が明示的にCookie: Hoge=Fugaと書かなくても半自動的にくっついてやりとりされていますよね。このことからも、SessionStorage や LocalStorage がユーザー体験のための小さな入れ物用に設計されているのに対し、Cookie はブラウザとサーバー間の通信のために利用する入れ物として設計されていることがわかりそうです。

ということで、よくよく SessionStorage や LocalStorage と一緒に比較してみると、性質はまるで別物ということに気づけました。

次に Cookie の属性について紹介します。
属性というのは、Cookie の発行時に Cookie の利用条件や制約のような形でつけられるものです。Cookie のセキュリティの機構についてありと先ほどお伝えしましたが、このセキュリティのための制約が主になります。

たとえば以下の Set-Cookie ヘッダーは、

Set-Cookieヘッダーの様子

  • path: "/"パス(つまりすべてのパスのリクエスト)において使っていいよ
  • expired: 有効期限は 2024/12/30 07:48 ごろだよ
  • httponly: httponly じゃないと使えないよ
  • samesite: strict の制約でないと使えないよ

ということを示しているということです。

Cookieストアの様子

Set-Cookie ヘッダーが返ってきたので、このサイト上で発行したものとして devtool 上でも確認することができました。

ではどのような属性が指定できるのか 1 つずつ見ていきます。

Secure, HttpOnly

先ほど Cookie にはセキュリティの機構がある、と SessionStorage や LocalStorage との違いのところでお伝えしましたが、そのセキュリティ対策の 1 つに当たるのが Secure 属性、HttpOnly 属性となります。

Secure 属性は、HTTPS 通信のリクエストでしか Cookie をセットしてリクエストしないようにできるよ、という属性です。
前述の際の画像は localhost での通信をしているため、この属性を true にすると Cookie の送信ができなくなってしまうため、指定していませんでした。

HTTP 通信は HTTPS 通信に比べると安全ではないため、「安全でないサイトから Cookie の送信しないでね」という制約になります。

HttpOnly は、Http 通信の時なら取得できても良いけれど、その他(JavaScript 等)では取得はできないようにできるよ、という属性です。この属性がつけられていない場合 JavaScript のdocument.cookieで取得ができてしまうため、XSS 対策に有用です。前述の通り、Cookie は基本的にバックエンド側で制御してくれるので、フロントエンド側でいじる必要はほぼなく、基本的にはつけます。

Domain, Path

続いて Domain と Path 属性です。Cookie を送信する対象を指定するものであり、この Domain と Path にしか Cookie 送信できないです、という制約をつけます。特に指定しない場合は発行元のドメインになりますが、サブドメインに関しては Cookie が送信されなくなってしまうため、もしサブドメインにも Cookie を送る必要がある場合は、Domain 属性に明示的にドメインを入れる必要があります。

Set-Cookie の仕様にある通り、基本的には Cookie の発行者が関係ないドメインを指定するのは無理です。そのため、発行元(リクエスト元)= Cookie を送信するターゲットになります。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie#domain

最初に SessionStorage や LocalStorage と比べた Cookie の特徴として、条件付きだがオリジン間で Cookie の情報を共用できる、というお話をしましたが、オリジン間でブラウザの Cookie に関する情報を、Domain 属性を指定することにより別のドメインに同じように飛ばすことができれば、だいぶ Cookie を利用して色々とできそうな気がしてきますよね。そこで思い出すのが、よくダイエットについて検索していると、特に覚えがない a.com の広告枠に、最近見ていたダイエットに関わる食事の広告が出てくる……あのちょっとゾワっとするやつです。

SameSite 属性が大きく関わってくるのですが、その理解のためには ファーストパーティ Cookie, サードパーティ Cookie の仕組みと違いを先に抑えるのが良さそうです。

ファーストパーティ Cookie は訪問中の Web サイトのオリジンによって発行された Cookie、サードパーティ Cookie はそれ以外によって発行された Cookie です。

ファーストパーティ Cookieとサードパーティ Cookie

上記の画像の場合、緑線に囲まれている範囲は Domain 属性が現在表示している FontAwesome のサイトとは異なるドメインのため、サードパーティ Cookie と呼ばれ、同じ Domain 属性のためファーストパーティ Cookie です。

ちなみに現在の Cookie はクロススキーマも異なるオリジンとして扱われます(http://example.com で発行されたものと https://example.com で発行された Cookie 全く別物として区別されるという意味)。

ちなみに、サードパーティ Cookie はなんで作られるのかめちゃくちゃ疑問だったのですが、調べてみると大まかには以下のような流れで作られているみたいです。例としてよくあるユーザートラッキングの仕組みで紹介します。

  • ユーザーが a.com のサイトに訪問する
  • a.com に埋め込まれた JavaScript のスクリプトタグが発火してトラッキングを管理するサーバーにリクエスト
  • トラッキングを管理するサーバーは 発行元と Domain 属性を hoge.track.com にして Cookie を作成する
  • ユーザーが b.com のサイトに訪問する
  • b.com に埋め込まれた JavaScript のスクリプトタグが発火してすでにある Cookie を利用して hoge.track.com と通信
  • サーバーはトラッキングしているユーザーが a.com と b.com にいたことを知る(この情報がチリツモすることによりどういうユーザーなのかだいたいわかる)

正直ふわっとなので、細かいところを間違えていたらすみません。大体こんな感じにして、サードパーティ Cookie を駆使してユーザー情報をあらゆるサイト訪問の履歴から追っているんですね。

(めちゃくちゃ余談ですが、fontawesome.com で発行されたタグの中に Google Analytics の計測用かな?みたいなキー名のファーストパーティ Cookie も仕込まれています。GA の中にはページ滞在時間や遷移の追尾など、fontawesome.com にアクセスした際の解析にしか利用しないものも存在しており、そういったものは fontawesome.com で発行してこのリクエストの際だけで処理するようにして調整しているのだな、と思ったりしました。)

fontawesome.com で発行しているgaキーの様子

とはいえこれちょっとユーザーのプライバシーどうなの?ということで サードパーティ Cookie を廃止しようという動きが活発になっているのが今日このごろの、この界隈です。私はこの手の騒動があった際には「え!? Cookie だめなの!? 全部ダメじゃん」って勘違いしていましたが、 サードパーティ Cookie について理解できたこんにちでは、最近の騒ぎについてもよりなるほど感が増したような気がしました。

SameSite

前置きが長くなってしまったのですが、SameSite 属性は サードパーティ Cookie を間接的に制御するような動きになります。
SameSite は属性によって、クロスサイト(= 異なるオリジン間)でのリクエストをする際に、異なるオリジン間で Cookie の共用を許可するかどうかを制御します。

  • Strict: クロスサイトでは許可しない
  • None: クロスサイトでも許可する。ただしこの値を指定するためには Secure 属性のセットが必要
  • Lax: ユーザーによるクリックなどの安全な手段の限り、許可する

Strict の場合は サードパーティ Cookie は使えません。None に指定しサードパーティ Cookie を利用するためには HTTPS の安全な通信が求められます。サードパーティ Cookie の利用に制約がかかっていると言えそうです。現在は属性が指定されない場合は、Lax という中間くらいの厳しさの制約になります。

開発においてはクロスサイトでのリクエストが発生するタイミングでいえば、ドメインが異なる 2 つのサービスで Cookie を利用した認証を行っており、かつ認証状態を共有しようとしている時などでしょうか? 1 つのドメインのサーバーとブラウザ同士で通信する限りは、Strict でもなんら構わなそうだなと思いました。

サードパーティ Cookie

サードパーティ Cookie をみてみると、SameSite 属性において None や指定なし(Lax)が指定されていること、None の場合は Secure 属性に必ずチェックが付いていることがわかります。また、Strict が 1 つありますが、こちらは fontawesome.com にいる限り Cookie に含めて送信されることはありません。

おわりに

Cookie はブラウザとサーバー間のやりとりに特化した、セキュリティの機構を持つ入れ物ということがよくわかりました。何となくみていた属性やリクエストなども、何を示しているのかや、どう見たら良いのかが、本記事を通してわかりやすくなっていれば嬉しいです。

ここまでお読みいただき、ありがとうございました!

参考文献

https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie

Discussion