この頁では、HTTP通信の状態管理に使用される「HTTP Cookies」の仕様とその使用例について解説します。
HTTPにおいて、クッキー{Cookie}とは、元々ユーザエージェント(Webブラウザ)によって保存される「小さな」ファイルを指します。 クッキーは、主に「状態管理」のために利用され、具体的には、たとえばオンラインショッピングでの「ショッピングカート」を実現するために利用されています。 クッキーは、専用のHTTPヘッダ、あるいはJavaScriptにより利用することができます。
クッキーが考え出された理由、つまり「クッキーは何故必要か?」という疑問に答えるためには、HTTPの性質を知る必要があります。 それは、HTTPでは“状態{State}”を保持することはできないという性質です。 ここでいう“状態”とは、「過去からのHTTPリクエスト-レスポンス結果を踏まえた“状態”」という意味なので、すなわち「“状態”を保持する」とは「前回のHTTPリクエスト結果を踏まえた上で、今回のHTTPリクエストを送信する」ということになります。 しかし、HTTPでは、ハイパーテキストの大量な要求に素早く対応できるようにするために、「あえて“状態”を持たないように」設計したのです。
ところが、WWWが爆発的に普及し、様々な場面でWebが(=HTTPが)利用されるようになるにつれて、“状態”を保持したいというHTTPの性質に反するニーズが出てきました。 そこで、当時WebブラウザやWebサーバを作っていたNetscape Communication社という会社が、「WebブラウザがあらかじめWebサーバから受け取った“状態”の情報をファイルに保存しておき、次回Webサーバと交信{Session}する時にWebブラウザがそれをWebサーバに送り返す」というアイデアを考えつき、当時のWebブラウザやWebサーバにこの技術を実装しました。 これがクッキーの始まりです。(※) クッキーを作ったNetscape Communication社という会社はすでに存在しませんが、クッキーという技術は他の多くのWebブラウザに実装され、現在も広く利用されています。
(※) 資料によると、1994年9月にリリースされたNatscape Navigatorのベータ版(Ver0.9 beta)には、すでにクッキーが実装されていたそうです。 詳しくは、David M. Kristol氏による論文“HTTP Cookies: Standards, Privacy, and Politics”を参照ください。
ただし、現在はすでにほとんどのHTTPアプリケーションで利用されている技術であるとはいえ、クッキーは正式にHTTPに組み込まれたものではないため、情報の提供者はクッキーを扱えない、あるいは(意図的に)扱わないクライアントにも、クッキーを扱う場合と同等なサービスを提供できるようにしておかなければいけません。
クッキーの仕様書は、2011年5月現在、全部で4種類あります。
上述の通り、クッキーは、元々Netscape Communication社が考えた技術です。 Netscape社は、クッキーを誰でも使えるように“PERSISTENT CLIENT STATE - HTTP COOKIES”という仕様書を公開していました。 この仕様は、Netscape社製のWebブラウザはもちろん、他社のWebブラウザ(Internet Explorerなど)もサポートしています。
しかし、Netscape Communication社は1998年にAOL社に買収され、また2008年にはNetscapeブラウザのサポートも終了してしまいました。 そのため、現在ではこの仕様書の「原本」を入手することはできません。(※)
(※) ただし、Netscape社以外の第三者によって取得された当時のリソースを、現在でも公開しているサイトがあります。 たとえば、インターネット・アーカイブでは、2011年5月現在、2002年8月3日にNetscape社から取得されたリソースを公開しています。 また、有志によって当時翻訳されたものが現在も公開されている場合があります。 日本語版としては、futomi社による日本語訳があります。
クッキーは有用な技術ですが、私企業による独自の技術であったため、これを標準化するための議論が、1995年にW3C内のメーリングリストで行われ始めました。 その結果、そこでの議論がまとめられ、1997年2月にD. KristolらによってHTTP State Management Mechanismという文書が記述されました。
RFC 2109のクッキー仕様は、その文書の最初の節で
ここで記述される方法はNetscapeのクッキー提案とは異なるが、Netscape方式を用いるHTTP/1.0ユーザエージェントと相互運用可能である
と記述されています。
ただし、RFC 2109仕様はNetscape仕様と異なるだけでなく、上位互換でもありません。
たとえばExpires属性がMax-Age属性に置き換わっていたり、新たにVersion属性が追加されています(差分については、RFC 2109の「歴史的経緯」の節を参照)
RFC 2109は、「Netscape版との相互運用」を図りながら、実際には「Netscape版によく似ているものの微妙に異なる仕様」となっています。 そのうえ、「微妙に異なる仕様」の実装が、当時の2大ブラウザであるNetscape NavigatorとMicrosoft Internet Explorerの間で異なっていたということがわかりました。 そこで、Kristolらは、この問題を解決するために、HTTP State Management Mechanismという同名文書を発行し、RFC 2109を廃版にするとしました。
問題解決の具体的な方法としては、新たにSet-Cookie2, Cookie2という別名のHTTPヘッダを定義することにしました。 RFC 2965のsection 9.1をご覧ください。
既存のクッキー実装は、Netscape の仕様に基づいているが、(Set-Cookie2 では無く) Set-Cookie ヘッダを使用する。 同じクッキーに対して Set-Cookie と Set-Cookie2 の両レスポンスヘッダを同じレスポンス中に受け取るユーザエージェントは Set-Cookie の情報を破棄し、Set-Cookie2 の情報のみを使用しなければならない。 なお、ユーザエージェントは、Set-Cookie2 レスポンスヘッダを受け取った場合、送信したサーバはこの文書に従い、またこの仕様書に従った Cookie リクエストヘッダも理解するであろうという事を仮定しなければならない。
新しいクッキーは同等の古い仕様のクッキーと新しい仕様のクッキーを置き換えなければならない。 これは、この仕様にも Netscape の元々の仕様にも従うユーザエージェントが Set-Cookie2 レスポンスヘッダを受け取り、NAME, Domain, Path 各属性が (クッキー管理の章に従って) 一致した場合、Netscape 仕様のクッキーは破棄されなければならない し、ユーザエージェントはこの仕様を守ったクッキーのみを保持しなければならない。
この仕様は理解しないが、Netscape の元々の仕様は理解するような古いユーザエージェントは Set-Cookie2 レスポンスヘッダを認識しないであろうし、古い仕様に従ってクッキーを送受信するであろう。
この仕様も Netscape 仕様のクッキーもサポートするユーザエージェントはクッキーを Set-Cookie2 レスポンスヘッダでは無く Set-Cookie レスポンスヘッダで受け取った場合、より古い Netscape 仕様に従う Cookie リクエストヘッダを送るべきである。 しかし、その上でユーザエージェントは以下のリクエストヘッダを送るべきである。
Cookie2: $Version="1"Cookie2 ヘッダは、サーバにユーザエージェントは新しい仕様のクッキーを理解するという事を通知する。 サーバが新しい仕様のクッキーを理解する場合も同様に、状態を持つセッションを、Set-Cookie よりも Set-Cookie2レスポンスヘッダによって続けるべきである。 新しい仕様のクッキーを理解しないサーバは単に Cookie2 リクエストヘッダを無視するであろう。
RFC 2965仕様に従うHTTPアプリケーションは、クッキーをやり取りする際に、相手に対して「Set-Cookie/CookieというNetscape仕様」と「Set-Cookie2/Cookie2というRFC 2965仕様」を同時に送信することになります。 HTTPでは、「自身が知らないHTTPヘッダは無視する」という大原則があるため、“旧仕様”と“新仕様”のヘッダを同時に送りつけた場合、過去のアプリケーションは“新仕様”という未知のHTTPヘッダは無視します。 一方、RFC 2965では「“新仕様”を常に優先すること」というルールを定めたので、新しいアプリケーションでは“旧仕様”を無視することができます。
RFC 2965仕様をすれば、従来よりもきめ細かくクッキーを制御することが可能になります(特にキャッシュへ保存する/保存しないの設定など)。 とはいえ、ユーザ側のメリットが見えにくかったせいか、実際にこの仕様はあまり広まりませんでした。
ここまでで、クッキーに関しては3種類の仕様書が作成されましたが、実際にはほとんどの場合Netscape版の仕様が利用されるという状況でした。 しかし、上述の通り、Netscape社はすでに無くなり、クッキーの仕様書はもはや通常の方法では手に入れられない状況になってしまいました。 この状況を受けて、2011年4月に、三たびHTTP State Management Mechanismという題名で、RFCが発行されました。 改めてクッキーの仕様書を発行した目的については、RFC 6265のsection 1をご覧ください。
本書は、これらのヘッダの構文と意味論を、それらが実際にインターネット上で利用されているように、規定する。 特に、本書では、それらが今日使用される構文や意味論を超える新たなものは作らない。 (中略)
本書の以前には、クッキーに関して少なくとも3つの記述があった: いわゆる“Netscape版クッキー仕様”[Netscape]、RFC 2109 [RFC2109]、RFC 2965 [RFC2965]である。 しかし、 これら文書のいずれも、CookieヘッダやSet-Cookieヘッダが実際にインターネット上でどのように使用されるかは記述していない(歴史的背景については[Kri2001]を参照)。 過去のHTTP状態管理メカニズムについてのIETF仕様に関して、本書は以下のような振る舞いを要求する:
- [RFC2109]の状態をHistoricに変更する(すでに[RFC2965]によって廃版にされている)
- [RFC2965]の状態をHistoricに変更する
- [RFC2965]は本書によって廃版とすることを表明する
特に、RFC 2965をHistoricに移行し、廃版とすることで、本書ではCookie2とSet-Cookie2のヘッダフィールドの使用を非推奨とする。
RFC 6265は、RFC 2109でNetscape版クッキーの仕様から削除されてしまったパラメータが復活していたり、またRFC 2965で定義はされていたものの現実的にはほとんど利用されていなかったSet-Cookie2, Cookie2が明確に非推奨とされたりと、「現状の運用をデファクトスタンダードとして、クッキーの仕様を再定義する」という明確な目的によって記述されています。 今後のHTTPアプリケーションの開発でクッキーの仕様を参照する場合などには、唯一RFC 6265のみが参照されることになるでしょう。
Netscape社が開発したクッキーという技術は、Set-CookieとCookieという2種類のHTTPヘッダのやりとりで状態を管理しようとするものです。 ここではその仕様(※)について、簡単に見ていきましょう。
(※) ここでは、現在最も広く利用されているであろうNetscape社の(オリジナル)クッキー仕様を元に記述しますが、将来的にはRFC 6265仕様に変更予定です。
クッキーは、まずサーバがSet-Cookieというヘッダを発行する所から始まります。 サーバがクライアントにクッキーを送る時のレスポンスヘッダの例をご覧下さい。
HTTP/1.1 200 OK Date: Sun, 03 Jun 2001 12:00:00 GMT Server: Apache/1.3.14 Set-Cookie: num=123456; expires=Sun, 10-Jun-2001 12:00:00 GMT; path=/HTTP/ Last-Modified: Fri, 01 Jun 2001 00:00:00 GMT Content-Length: 999 Content-Type: text/html
Netscape社のクッキーにおける、Set-Cookieヘッダのフォーマットは以下の形式となります。
Set-Cookie:NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
各属性について、以下をご覧ください。
NAME=VALUE
クッキーの名前とその値を指定します。
Set-Cookieでは、唯一の必須属性です。
NAMEやVALUEには、セミコロン、コンマ、空白文字を除いた文字列を指定することができます。
セミコロン、コンマ、空白文字等を用いたい場合は、何らかのエンコードが為されることが推奨されています。
上の例では、クッキーの名前がnumで、その値が123456です。
DATEクッキーの有効期限を設定します。 有効期限を経過したクッキーはクライアントから削除されるか、あるいはサーバへ送り返されてはいけません。 有効期限の日付文字列フォーマットは以下の通りです。
Wdy, DD-Mon-YYYY HH:MM:SS GMT
expires 属性が省略されたクッキーは、クライアントとサーバの接続が終了した時に削除されます。
また、Dateが過去の日付を持っているクッキーは、そのクッキーを受け取った時点で破棄されます。
PATHクッキーを送り返すURLのサブセットを指定します。 リクエストを送るURLにpath条件が一致した場合に、クッキーは有効とみなされ、リクエストと一緒に送られます。 path属性は、前から順に一致しているかどうかをチェックします(前方一致)。 したがって、たとえば/fooというpathは、/foobarや/foo/bar.htmlに一致します。 /というpathは、そのサーバ内の全てのリソースに一致します。
pathが省略されたクッキーは、そのクッキーを含んでいるヘッダによって記述されているドキュメントと同じpathが指定されたとみなされます。
DOMAIN_NAMEクッキーを送り返すサーバのドメイン名を指定します。 リクエストを送るURLにdomain条件が一致した場合に、クッキーは有効とみなされ、リクエストと一緒に送られます。 domain属性は、後ろから順に一致しているかどうかをチェックします。 従って例えば、.acme.comというdomainは、anvil.acme.comやshipping.crate.acme.comに一致します。
DOMAIN_NAMEに指定できる文字列は、トップレベルドメインが“com”,“edu”,“net”などの一般トップレベルドメイン (gTLD)の場合はピリオドが2つ以上、それ以外の場合はピリオドが3つ以上含まれていなければいけません。
(※) 上記の理由により、domain=studyinghttp.netなどと指定することはできません。 何故ならば、domain属性は後方一致のため、www.evilstudyinghttp.netというドメインにも一致してしまうからです。 これを防ぐために、ドメイン指定はdomain=.studyinghttp.netとしなければならないのです。
domainが省略されたクッキーは、そのクッキーを生成したサーバのドメイン名が指定されたとみなされます。
クッキーがsecureと指定されていた場合、そのクッキーは、例えばそれが暗号化される等して、サーバとの通信が安全に行われる場合にのみ送られます。 この暗号化の一つに、Netscape Communication社が開発したSSLがあり、SSLを用いてHTTPを利用するものをHTTPSと呼びます。 現在においてsecureとは、その通信がHTTPSに基づいて行われる場合を意味します。
secure指定がないクッキーは、安全とみなされ、通常の通信(HTTP)を通して明文で送られます。
また、以下の様に一つのサーバレスポンス中に複数のSet-Cookieヘッダを発行することもできます。
Set-Cookie: param1=ABCDEF; expires=Sun, 10-Jun-2001 12:00:00 GMT; path=/HTTP/ Set-Cookie: param2=GHIJKL; expires=Mon, 31-Dec-2001 23:59:59 GMT; path=/
(※) RFC 2109のクッキーでは、expires属性の代わりに、クッキーの寿命秒数を表すMax-Age属性が設けられました。
Set-Cookieヘッダを受け取ったクライアントは、送り返す条件に一致したクッキーについては、リクエストメッセージと共にCookieというヘッダを発行します。 Cookieヘッダのフォーマットは以下の様になります。
Cookie:NAME1=OPAQUE_STRING1;NAME2=OPAQUE_STRING2; ...
このように、条件に合ったクッキーが“名前=非空白文字列”の形式で与えられます。 クッキーが複数ある場合は“;”で区切られます。
(※) RFC 2109 のクッキーでは、“;”の他に“,”も使用できるようになりました。 サーバは将来の互換性のために区切り子として“,”も受け入れるべきです。
複数のクッキーを送る場合、より詳細な(長い)Path属性を持つものがそうで無いものよりも先に来るように Cookie ヘッダの中で順序付けられます。 但し、他の属性(ドメインやパスなど)に関する順序付けは定義されていません。
サーバからクライアントへクッキーが送られる場合に、expires属性による有効期限内のクッキーは保存されます。 しかし、クライアントが保存することができるクッキーの数には以下のような制限があります。
NAMEとVALUEを合わせて、最大で4キロバイトまでもし1.や3.の制限を越えた場合、クライアントは保存されているクッキーを調べ、最も過去に使用されたクッキーから順に削除するかもしれません。 また、2.の制限を越えるようなクッキーを受け取った場合、クライアントは4キロバイト以内に収まる様にクッキーを削るかもしれません。 そのため、サーバはそのような制限を超えるクッキーを発行すべきではありません。
次のクッキーの使用例をご覧下さい。
リクエストやレスポンスの各ヘッダの詳細部は省略しています。
POST /shopping/login HTTP/1.1 [form data]
ユーザはフォームを通して個人を識別するために個人情報を入力します。
HTTP/1.1 200 OK Set-Cookie: Customer="Tarou_YAMADA"; Path="/shopping";
クッキーがユーザの個人情報を反映します。
POST /shopping/pickitem HTTP/1.1 Cookie: Customer="Tarou_YAMADA"; [form data]
以降、クライアントがこのサーバ上の path "/shopping" 以下の URL にリクエストをする時には、上のクッキーをサーバに送ります。
HTTP/1.1 200 OK Set-Cookie: Part_Number="IBMPC_01"; Path="/shopping"
クライアントが /shopping/pickitem で品目を選んだとして、サーバはその品目を含めます。
POST /shopping/shipping HTTP/1.1 Cookie: Customer="Tarou_YAMADA"; Part_Number="IBMPC_01"; [form data]
以降、クライアントがこのサーバ上の path "/shopping" 以下の URL にリクエストをする時には、上のクッキーをサーバに送ります。
HTTP/1.1 200 OK Set-Cookie: Shipping="Yamato"; Path="/shopping"
クライアントが /shopping/shipping で発送方法を選んだとして、サーバはその発送方法を反映します。
POST /shopping/process HTTP/1.1 Cookie: Customer="Tarou_YAMADA"; Part_Number="IBMPC_01"; Shipping="Yamato"; [form data]
以降、クライアントがこのサーバ上の path "/shopping" 以下の URL にリクエストをする時には、上のクッキーをサーバに送ります。
HTTP/1.1 200 OK
通信処理は完了します。
ユーザエージェントは、新しいクッキーを受け取る度に、オリジンサーバ上に一続きのリクエストを作っています。 すべてのクッキーは、同じ Path 属性と、ここでは指定されていない既定ドメインを持っています。 Request-URI はすべて、それぞれのクッキーの Path 属性である /shopping とパス一致しているので、それぞれのリクエストはそこまでに受信したすべてのクッキーを含んでいます。
(※) この例では、HTTP/1.1既定の振る舞いである持続的接続がサポートされているということに注意して下さい。 これによって、クッキーにはexpires属性が設定されていません。
この例では Path 属性の効果を説明します。 リクエストとレスポンス各ヘッダの詳細部はすべて省略しています。
この時、ユーザエージェントはこれ以前のリクエストに対するレスポンスで、以下の二つのレスポンスヘッダを受信していると仮定します。
Set-Cookie: Part_Number="Rocket_Launcher_0001"; Path="/acme" Set-Cookie: Part_Number="Riding_Rocket_0023"; Path="/acme/ammo"
以降、クライアントがこのサーバ上の path "/acme/ammo" 以下の URL にリクエストをする時には、以下のクッキーをサーバに送ります。
Cookie: Part_Number="Riding_Rocket_0023"
つまり、より詳細な Path 属性である“/acme/ammo”を持つクッキーの NAME=VALUE ペアが、比較して詳細でないPath属性である“/acme”のものを上書きします。
但し、クライアントがこのサーバ上の“/acme/parts”以下のURLにリクエストをする時には、以下のクッキーをサーバに送るでしょう。
Cookie: Part_Number="Rocket_Launcher_0001"
ここで、二番目のクッキーの Path 属性“/acme/ammo”は、リクエストURLである“/acme/parts”の中には含まれないので、このクッキーはサーバに転送されません。
インターネットにおける個人情報の漏洩等のセキュリティ問題を考える時に、クッキーに関心を持つかもしれません。 クッキーはその性質上、ユーザの区別・追跡・嗜好の調査等に使われますから、これに嫌悪感を持つかもしれません。 しかし、追跡といってもクッキーでは前回アクセスした人と今回アクセスした人が同一であるかどうかしかわかりません。
確かに、クッキーにはあなたの個人情報が入っている場合があります。 あなたがあるサイトで個人情報を入力した場合、それらがクッキーとして保存される可能性があるからです。 では、第三者があなたのそのクッキーを見ることができるのでしょうか?
例えば、悪意あるサーバ管理者があなたのクッキーを読もうとした場合はどうでしょうか? クッキーの仕様を正しく実装したクライアントでは、この問題は起きません。 それぞれのクッキーは、それを発行したサーバーでなければ受け取ることができないからです。 つまり、abc.comが発行したクッキーはxyz.comに返されることは無いのです。
しかし、path 属性のいいかげんなクッキーは同一ドメイン内の別のユーザにクッキーを盗み読まれる可能性があります。 これは、クッキーの発行者の問題です。 クッキーの発行者は、クッキーのセキュリティ問題をも考えて、クッキーを発行しなければいけません。 例えば、プライバシーに関わるようなクッキーのデータは暗号化し、第三者が読んでもそれがどんな意味があるのかがわからないようにすべきです。
実際問題として、インターネットの安全性に神経質になるならば、サイト上のクイズに応募したり、ショッピングをすることはできなくなるでしょう。 プライバシー漏洩の可能性としては、たとえクッキーを用いなかったとしても、その情報を得た会社がそれをデータベースとして他の会社に売るようなことまでもが考えられるからです。 つまり、そのクッキーを信用するかどうかということは、結局その先に居るクッキーの管理者を信用するかどうかということに他なりません。 従って、そのサイトの内容が信用できない時は、当然そのサイトが発行するクッキーも信用すべきではありません。
またクッキーの発行者は、信用されるようなクッキーを発行するために、以下の点に気をつけるべきです。
ユーザエージェントの実装者は、クッキーに関心を持つクライアントのために、クッキーを受け取った時に警告を出させ、選択によってはそれを受け入れないようにするようなオプションを実装すべきです。