Cookie とは、サーバ上のある“状態”における情報をブラウザに保存させ、次回の“交信(セッション)”時にそれをブラウザがサーバに送り返すという技術で、専用の HTTP ヘッダ、あるいは JavaScript により利用する事ができます。
Cookie は、元々 Netscape Communication 社によって開発されました。(Client Side State - HTTP Cookies)。 しかし、現在では Netscape 社以外のブラウザもこの技術をサポートしており、1997 年 2 月に発行された RFC 2109 で Set-Cookie と Cookie という既に使用されている HTTP ヘッダを、更に 2000 年 10 月に発行された RFC 2965 で Set-Cookie2 と Cookie2 という新しい HTTP ヘッダを利用した状態管理メカニズムを提案しています。
(※) 但し、現在のところ、RFC 2109 及び RFC 2965 の技術を実装している HTTP アプリケーションはほとんどありません。 従って現実の HTTP 開発者は、これらの仕様をあまり気に止める必要は無いでしょう。
Cookie は何故必要なのでしょうか? その理由は、HTTP の性質に深く関わっています。
HTTP では、基本的に状態を保持する事はできません。 すなわち、サーバは「あるユーザエージェントの前回のリクエスト結果を踏まえた上で、今回のリクエストを処理する」というような事はできません。 これは、HTTP では FTP 等とは違って、ハイパーテキストの大量な要求に対応できるように処理を単純化しようとした HTTP が持つ性質でもあります。
ところが、WWW が爆発的に普及し、様々な場面で HTTP が利用されるにつれて、状態が保持できないという HTTP の性質が不便であるようになる場合が出てきました。 そこで、状態を管理するために、例えばリソースの URL 中に任意の動的に生成されるパラメータをエンコードしておくといった代替手段が取られてきました。 しかし、この方法ではURL と一緒にセッションに関する状態情報がキャッシュに保存され、結果的に第三者に漏洩する可能があります。 また状態を URL によって維持すると、同一内容のリソースがいくつかキャッシュの中に存在する事になるでしょう。
そこで、URL 等の代替手段では無く、ユーザの状態を保持するために開発された技術が Cookie なのです。 この技術は、例えば WWW 上でのサービスにおけるユーザの関心を記憶させたり、オンラインショッピングでの「ショッピングカート」的役割を行わせるために有用で、その状態を表すためには本来ならば全てサーバ側で保存すべき情報の一部を、クライアントに保存させる事でその交信の効率を上げています。
しかし、Cookie は正式に HTTP に組み込まれたものではないので、情報の提供者は Cookie を扱えない、あるいは扱わないクライアントにも Cookie を扱う場合と同等なサービスを提供できるようにしておかなければいけません。
Netscape 社が開発した Cookie という技術は、Set-Cookie と Cookie という二種類の HTTP ヘッダのやりとりで状態を管理しようとするものです。 ここではその仕様について、簡単に見ていきましょう。
Cookie は、まずサーバが Set-Cookie というヘッダを発行する所から始まります。 サーバがクライアントに 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 社 の Cookie における、Set-Cookie ヘッダのフォーマットは以下の様になります。
Set-Cookie:NAME
=VALUE
; expires=DATE
; path=PATH
; domain=DOMAIN_NAME
; secure
各属性についてを、以下に示します。
NAME
=VALUE
Cookie の名前とその値を指定します。
Set-Cookie では、唯一の必須属性です。
NAME
や VALUE
には、セミコロン、コンマ、空白文字を除いた文字列を指定する事ができます。
セミコロン、コンマ、空白文字等を用いたい場合は、何らかのエンコードが為される事が推奨されています。
上の例では、Cookie の名前が num で、その値が 123456 です。
DATE
Cookie の有効期限を設定します。 有効期限を経過した Cookie はクライアントから削除されるか、あるいはサーバへ送り返されてはいけません。 有効期限の日付文字列フォーマットは以下の通りです。
Wdy, DD-Mon-YYYY HH:MM:SS GMT
expires 属性が省略された Cookie は、クライアントとサーバの接続が終了した時に削除されます。
また Date
が過去の日付を持っている Cookie は、その Cookie を受け取った時点で破棄されます。
PATH
Cookie を送り返す URL のサブセットを指定します。 リクエストを送る URL に path 条件が一致した場合に、 Cookie は有効とみなされ、リクエストと一緒に送られます。 path 属性は、前から順に一致しているかどうかをチェックします。 従って例えば、/foo という path は、/foobar や /foo/bar.html に一致します。 / という path は、そのサーバ内の全てのリソースに一致します。
path が省略された Cookie は、その Cookie を含んでいるヘッダによって記述されているドキュメントと同じ path が指定されたとみなされます。
DOMAIN_NAME
Cookie を送り返すサーバのドメイン名を指定します。 リクエストを送る URL に domain 条件が一致した場合に、 Cookie は有効とみなされ、リクエストと一緒に送られます。 domain 属性は、後ろから順に一致しているかどうかをチェックします。 従って例えば、acme.com という domain は、anvil.acme.com や shipping.crate.acme.com に一致します。
DOMAIN_NAME
に指定できる文字列は、トップレベルドメインが "com", "edu", "net", "org", "gov", "mil", "int" の場合はピリオドが2つ以上、それ以外の場合はピリオドが3つ以上含まれていなければいけません。
従って例えば、domain=jp 等と指定する事はできません。
domain が省略された Cookie は、その Cookie を生成したサーバのドメイン名が指定されたとみなされます。
Cookie が secure と指定されていた場合、その Cookie は、例えばそれが暗号化される等して、サーバとの通信が安全に行われる場合にのみ送られます。 この暗号化の一つに、Netscape Communication 社が開発した SSL があり、SSL を用いて HTTP を利用するものを HTTPS と呼びます。 現在において secure とは、その通信が HTTPS に基づいて行われる場合を意味します。
secure 指定がない Cookie は、安全とみなされ、通常の通信 (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 の Cookie では、expires 属性の代わりに、Cookie の寿命秒数を表す Max-Age 属性が設けられました。
Set-Cookie ヘッダを受け取ったクライアントは、送り返す条件に一致した Cookie については、リクエストメッセージと共に Cookie というヘッダを発行します。 Cookie ヘッダのフォーマットは以下の様になります。
Cookie:NAME1
=OPAQUE_STRING1
;NAME2
=OPAQUE_STRING2
; ...
このように、条件に合った Cookie が "名前=非空白文字列" の形式で与えられます。 Cookie が複数ある場合は ";" で区切られます。
(※) RFC 2109 の Cookie では、";" の他に "," も使用できるようになりました。 サーバは将来の互換性のために区切り子として "," も受け入れるべきです。
複数の Cookie を送る場合、より詳細な (長い) Path 属性を持つものがそうで無いものよりも先に来るように Cookie ヘッダの中で順序付けられます。 但し、他の属性 (Domain 等) に関する順序付けは定義されていません。
サーバからクライアントへ Cookie が送られる場合に、expires 属性による有効期限内の Cookie は保存されます。 しかし、クライアントが保存する事ができる Cookie の数には以下のような制限があります。
NAME
と VALUE
合わせて、最大で 4 キロバイトまでもし 1 や 3 の制限を越えた場合は、クライアントは保存されている Cookie を探査し、最も過去に使用された Cookie から順に削除されるでしょう。 2 の制限を越えるような Cookie を受け取った場合、クライアントは 4 キロバイト以内に収まる様に Cookie を削るでしょう。
サーバは、クライアントがこれらの制限を超えている事を期待してこの制限以上の Cookie を発行すべきではありません。
次の Cookie の使用例をご覧下さい。
リクエストやレスポンスの各ヘッダの詳細部は省略しています。
POST /shopping/login HTTP/1.1 [form data]
ユーザはフォームを通して個人を識別するために個人情報を入力します。
HTTP/1.1 200 OK Set-Cookie: Customer="Tarou_YAMADA"; Path="/shopping";
Cookie がユーザの個人情報を反映します。
POST /shopping/pickitem HTTP/1.1 Cookie: Customer="Tarou_YAMADA"; [form data]
以降、クライアントがこのサーバ上の path "/shopping" 以下の URL にリクエストをする時には、上の Cookie をサーバに送ります。
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 にリクエストをする時には、上の Cookie をサーバに送ります。
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 にリクエストをする時には、上の Cookie をサーバに送ります。
HTTP/1.1 200 OK
通信処理は完了します。
ユーザエージェントは、新しい Cookie を受け取る度に、オリジンサーバ上に一続きのリクエストを作っています。 すべての Cookie は、同じ Path 属性と、ここでは指定されていない既定ドメインを持っています。 Request-URI はすべて、それぞれの Cookie の Path 属性である /shopping とパス一致しているので、それぞれのリクエストはそこまでに受信したすべての Cookie を含んでいます。
(※) この例では HTTP/1.1 の既定の振る舞いである持続的接続がサポートされている事に注意して下さい。 これによって、Cookie には 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 をサーバに送ります。
Cookie: Part_Number="Riding_Rocket_0023"
つまり、より詳細な Path 属性である "/acme/ammo" を持つ Cookie の NAME=VALUE ペアが、比較して詳細でない Path 属性である "/acme" のものを上書きします。
但し、クライアントがこのサーバ上の path "/acme/parts" 以下の URL にリクエストをする時には、以下の Cookie をサーバに送るでしょう。
Cookie: Part_Number="Rocket_Launcher_0001"
ここで、二番目の Cookie の Path 属性 "/acme/ammo" は、リクエストURL である "/acme/parts" の中には含まれないので、この Cookie はサーバに転送されません。
インターネットにおける個人情報の漏洩等のセキュリティ問題を考える時に、 Cookie に関心を持つかもしれません。 Cookie はその性質上、ユーザの区別・追跡・嗜好の調査等に使われますから、これに嫌悪感を持つかもしれません。 しかし、追跡といっても Cookie では前回アクセスした人と今回アクセスした人が同一であるかどうかしかわかりません。
確かに、Cookie にはあなたの個人情報が入っている場合があります。 あなたがあるサイトで個人情報を入力した場合、それらが Cookie として保存される可能性があるからです。 では、第三者があなたのその Cookie を見る事ができるのでしょうか?
例えば、悪意あるサーバ管理者があなたの Cookie を読もうとした場合はどうでしょうか? Cookie の仕様を正しく実装したクライアントでは、この問題は起きません。 それぞれの Cookie は、それを発行したサーバーでなければ受け取る事ができないからです。 つまり、abc.com が発行した Cookie は xyz.com に返される事は無いのです。
しかし、path 属性のいいかげんな Cookie は同一ドメイン内の別のユーザに Cookie を盗み読まれる可能性があります。 これは、 Cookie の発行者の問題です。 Cookie の発行者は、 Cookie のセキュリティ問題をも考えて、 Cookie を発行しなければいけません。 例えば、プライバシーに関わるような Cookie のデータは暗号化し、第三者が読んでもそれがどんな意味があるのかがわからないようにすべきです。
実際問題として、インターネットの安全性に神経質になるならば、サイト上のクイズに応募したり、ショッピングをする事はできなくなるでしょう。 プライバシー漏洩の可能性としては、たとえ Cookie を用いなかったとしても、その情報を得た会社がそれをデータベースとして他の会社に売るような事までもが考えられるからです。 つまり、その Cookie を信用するかどうかという事は、結局その先に居る Cookie の管理者を信用するかどうかという事に他なりません。 従って、そのサイトの内容が信用できない時は、当然そのサイトが発行する Cookie も信用すべきではありません。
またクッキーの発行者は、信用されるような Cookie を発行するために、以下の点に気をつけるべきです。
ユーザエージェントの実装者は、Cookie に関心を持つクライアントのために、Cookie を受け取った時に警告を出させ、選択によってはそれを受け入れないようにするようなオプションを実装すべきです。