mikanmarusanのブログ

テクノロジーとかダイビングとか

Cookieのexpireの扱いがブラウザによってちょっと異なる件

弊社のアプリが動かないという残念な問い合わせがいくつかあったので、1日ばかりずっと悶々と調べてみたりした。結局Cookieの挙動によるものだったんだけど、Cookieの有効期限の挙動がブラウザごとに違っているらしく、ちょっとハマッたので備忘録としてまとめてみた。

Cookieの仕様のおさらい

Cookieは、HTTP State Management Mechanism(HTTP 状態管理メカニズム)というタイトルで、RFC2965で定義されているみたいだが、各種ブラウザの実装は、Cookieを導入したNetscape Navigatorの実装に基づいているようだ。

ここでは、Cookieの仕様についての説明は割愛するが、超簡単にまとめると以下の通り。

  • サーバ
    • クライアントに保持してもらいたい状態情報を、HTTPレスポンスのSet-Cookieヘッダに乗せて送出する

Set-Cookie: NAME1=VALUE1; expires=DATE; path=PATH; domain=DOMAIN_NAME;
Set-Cookie: NAME2=VALUE2; expires=DATE; path=PATH; domain=DOMAIN_NAME;

  • クライアント
    • サーバから送られてきた状態情報を(有効期限内であれば)保持し、次回以降のリクエストにおいて、HTTPリクエストの"Cookie:"ヘッダに乗せて送出する

Cookie: NAME1=VALUE1; NAME2=VALUE2 ...

ここらへんは、Webサイトを構築するエンジニアは必須知識だと思う。

まずはCookieの動きを確認する

Net Applicationsによれば、2010年12月のブラウザ3大シェアは下記の通り。ということで下記3つのブラウザを調査対象とする。ちなみにクライアントはVista。あまり定評が良くないVista

ここで、検証に利用する予定のSet-Cookieするサーバ側のアプリケーションをさらしておく(PHPerですみません)。有効期限が600秒(=10分間)のtestcookieという名前のCookieを、phpのsetcookie関数で発行する単純なプログラムである。

[cookieを生成するサーバアプリケーション] http://192.168.1.101/setcookie.php
  1 <?php
  2     $currentTime = time();
  3
  4     $cookieName   = "testcookie";
  5     $cookiePath   = "/";
  6     $cookieDomain = $_SERVER['SERVER_NAME'];
  7     $cookieValue  = strval($currentTime);       // value is issue time (Unix Time)
  8     $cookieExpire = strval($currentTime + 600); // expire is 10 minutes after issuing cookie
  9
 10     setcookie($cookieName,
 11               $cookieValue,
 12               $cookieExpire,
 13               $cookiePath,
 14               $cookieDomain);

ついでにサーバ情報はこんな感じ。

  • ホスト
    • 192.168.1.101 (プライベートIPでごめんなさい)
  • OS
  • 各種アプリ

ここでは、上記cookie発行プログラム(http://192.168.1.101/setcookie.php)にリクエストを送り、そのレスポンスヘッダとブラウザ側に蓄積されるcookie情報をブラウザごとに観察してみる。

Internet Explorer

通常のIEだとヘッダとかcookieの参照とか非力すぎて死ぬ。ヘッダ情報にはieHTTPHeadersCookieの参照にはInternet Explorer Developer Toolbar を導入した。Internet Explorer Developer ToolbarはMS純正。

  • ヘッダ情報

予想通り、Dateヘッダの10分後のExpireが設定されているtestcookieが発行され、それがブラウザにも保存されているようだ。当たり前か。

FireFox

FireFoxでヘッダの情報を確認するには、Live HTTP HeadersのAddonを導入。

  • ヘッダ情報

予想通り、Dateヘッダの10分後のExpireが設定されているtestcookieが発行され、それがブラウザにも保存され(略

Chrome

Chromeは、Ctrl+Shift+i でcookieやらヘッダ情報やら全て見える。デフォルトで使えて便利便利。

  • ヘッダ情報

予想通り、Dateヘッダの10分後のExpireが設定されているtestcookieが発行され(略。しつこいですね、わかります。

クライアントとサーバの時刻を変えてみる

クライアントとサーバの時刻が同じとき(=同期しているとき)、CookieのExpire値の処理はブラウザを変えても変化はなかった。クライアントの時刻がサーバと同期されていない場合はどうだろうか?サーバの時刻が不正な場合なんて言語道断なので、クライアントの時刻を1時間早めた場合で検証してみる。検証内容は先ほどと同様で、10分間の有効期限を設定したcookieを発行したときのブラウザごとの挙動を確認してみる。

Internet Explorer
  • ヘッダ情報 (クライアントのリクエスト時刻 = Mon, 31 Jan 2011 14:15:13 GMT ※1時間進んでるから)

あれ、cookieが蓄積されていない。試しに同じドメインで別のHTMLをGETするときのリクエストヘッダを見てみる。もしCookieが有効ならば"Cookie:"ヘッダがサーバに向けて付与されているはずである。

  • リクエストヘッダ情報

やっぱり、"Cookie:"ヘッダは送出されていない。この結果から推測されることは、IEでは"Set-Cookie:"のExpires値がそのまま評価され、クライアントのシステム時刻と比べて有効期限切れ(有効期限が過去の時刻になっている)と判断し、cookieに保存されなかったのではなかろうか?

FireFox
  • ヘッダ情報 (クライアントのリクエスト時刻 = Mon, 31 Jan 2011 14:34:15 GMT ※1時間進んでるから)

FireFoxcookie情報が蓄積されているようだ。しかもクライアント側のリクエスト発行時刻+10分間の有効期限が設定されていることに注意したい。また、IEの場合と同様に別のHTMLをGETするときのリクエストヘッダを観察してみる。cookieの有効期限の10分間を意識して、cookie発行から10分以内と10分経過後を比べてみた。

  • リクエストヘッダ情報(リクエスト時刻=cookie発行から10分以内)

  • リクエストヘッダ情報(リクエスト時刻=cookie発行から10分経過後)

この結果から推測されることは、Firefoxではサーバの意図したcookieの有効期限(=10分)がクライアント側の時刻に変換されてcookieに保存されるようである。おそらくレスポンスヘッダのDateヘッダとSet-CookieのExpires値の差分(=10分)を取得し、クライアントのシステム時刻(ただし今回は1時間進んでいる)に加えていると思われる。

Chrome
  • ヘッダ情報 (クライアントのリクエスト時刻 = Mon, 31 Jan 2011 14:50:17 GMT ※1時間進んでるから)

Chromeは、IEと同じ挙動をするようだ。

まとめ

3大ブラウザのCookieのexpires値の処理
  • Internet Explorer/Chrome
    • HTTPレスポンスに含まれるSet-Cookieヘッダーのexpiresの値を厳密に利用しているようだ。
  • Firefox
    • FireFoxは少し複雑な処理をしているように見受けられる。
      • 1) HTTPレスポンスに含まれるDateヘッダとSet-Cookieのexpiresの差を計算しサーバ側で意図したcookieの有効期限を計算しているのだろう。
      • 2) クライアントのシステム時刻に1)で取得した有効期限を足した値をexpireとしているように見受けられる。

通常の利用がなされる場合には、両者の挙動にはまったく違いが見られないが、サーバとクライアントで時刻が異なる特殊なケースの場合に挙動が異なる可能性が出てくる。

クライアントとサーバの時刻が同期されていないとき

例としてクライアントが1時間時計を進めている時に、有効期限10分のcookieを発行した場合を考える。

  • サーバ時刻
    • Mon, 31 Jan 2011 10:00:00 GMT
  • クライアント時刻
    • Mon, 31 Jan 2011 11:00:00 GMT (1時間進んでる)
  • cookie発行プログラムのレスポンス
    • Date: Mon, 31 Jan 2011 10:00:00 GMT
    • Set-Cookie: testcookie=1296481564; expires=Mon, 31-Jan-2011 10:10:00 GMT; path=/; domain=192.168.1.101

この場合は下記の挙動となる

  • Internet Explorer/Chrome
    • Set-CookieのExpire値(expires=Mon, 31-Jan-2011 10:10:00 GMT;)とクライアントのシステム時刻を比較
    • 過去の有効期限のCookieと判断され、Cookieは保存されない。
  • Firefox
    • DateとSet-CookieのExpire値からサーバ側が意図したCookieの有効期限(=10分)を割り出す
    • システム時刻と有効期限を加えて、有効期限が Mon, 31 Jan 2011 11:10:00 GMT であるようなCookieを蓄積する

結論

通常利用ではブラウザによるSet-CookieのExpire値の挙動に変化はないが、クライアントとサーバの時刻が同期されていないときは注意が必要かもしれない。