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。
- 対象ブラウザ
- Internet Explorer
- Internet Explorer 8(8.0.6001.18999)
- Firefox
- Firefox 3(3.6.13)
- Chrome
- Google Chrome(8.0.502.237)
- Internet Explorer
ここで、検証に利用する予定の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の参照とか非力すぎて死ぬ。ヘッダ情報にはieHTTPHeaders、Cookieの参照にはInternet Explorer Developer Toolbar を導入した。Internet Explorer Developer ToolbarはMS純正。
- ヘッダ情報
- cookie情報
予想通り、Dateヘッダの10分後のExpireが設定されているtestcookieが発行され、それがブラウザにも保存されているようだ。当たり前か。
FireFox
FireFoxでヘッダの情報を確認するには、Live HTTP HeadersのAddonを導入。
- ヘッダ情報
- cookie情報
予想通り、Dateヘッダの10分後のExpireが設定されているtestcookieが発行され、それがブラウザにも保存され(略
クライアントとサーバの時刻を変えてみる
クライアントとサーバの時刻が同じとき(=同期しているとき)、CookieのExpire値の処理はブラウザを変えても変化はなかった。クライアントの時刻がサーバと同期されていない場合はどうだろうか?サーバの時刻が不正な場合なんて言語道断なので、クライアントの時刻を1時間早めた場合で検証してみる。検証内容は先ほどと同様で、10分間の有効期限を設定したcookieを発行したときのブラウザごとの挙動を確認してみる。
Internet Explorer
- ヘッダ情報 (クライアントのリクエスト時刻 = Mon, 31 Jan 2011 14:15:13 GMT ※1時間進んでるから)
- cookie情報
あれ、cookieが蓄積されていない。試しに同じドメインで別のHTMLをGETするときのリクエストヘッダを見てみる。もしCookieが有効ならば"Cookie:"ヘッダがサーバに向けて付与されているはずである。
- リクエストヘッダ情報
やっぱり、"Cookie:"ヘッダは送出されていない。この結果から推測されることは、IEでは"Set-Cookie:"のExpires値がそのまま評価され、クライアントのシステム時刻と比べて有効期限切れ(有効期限が過去の時刻になっている)と判断し、cookieに保存されなかったのではなかろうか?
FireFox
- ヘッダ情報 (クライアントのリクエスト時刻 = Mon, 31 Jan 2011 14:34:15 GMT ※1時間進んでるから)
- cookie情報
FireFoxはcookie情報が蓄積されているようだ。しかもクライアント側のリクエスト発行時刻+10分間の有効期限が設定されていることに注意したい。また、IEの場合と同様に別のHTMLをGETするときのリクエストヘッダを観察してみる。cookieの有効期限の10分間を意識して、cookie発行から10分以内と10分経過後を比べてみた。
この結果から推測されることは、Firefoxではサーバの意図したcookieの有効期限(=10分)がクライアント側の時刻に変換されてcookieに保存されるようである。おそらくレスポンスヘッダのDateヘッダとSet-CookieのExpires値の差分(=10分)を取得し、クライアントのシステム時刻(ただし今回は1時間進んでいる)に加えていると思われる。
まとめ
3大ブラウザのCookieのexpires値の処理
- Internet Explorer/Chrome
- HTTPレスポンスに含まれるSet-Cookieヘッダーのexpiresの値を厳密に利用しているようだ。
- Firefox
通常の利用がなされる場合には、両者の挙動にはまったく違いが見られないが、サーバとクライアントで時刻が異なる特殊なケースの場合に挙動が異なる可能性が出てくる。
クライアントとサーバの時刻が同期されていないとき
例としてクライアントが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
この場合は下記の挙動となる
結論
通常利用ではブラウザによるSet-CookieのExpire値の挙動に変化はないが、クライアントとサーバの時刻が同期されていないときは注意が必要かもしれない。