読者です 読者をやめる 読者になる 読者になる

Paradigm Shift Design

ISHITOYA Kentaro's blog.

IE8でセッションがブツブツ切れる現象について

えー。
CollabTechも無事終わり、オーストラリアから帰国して気がつけば、1週間ちょい。
人間、喉元過ぎれば熱さ忘れるなので、気をつけます。


で、タイトルの通り、IE8でセッションがぶちぶちきれる現象に遭遇し原因を探るのに時間がかかってしまいました。
結論から言うと私のプログラムが悪かったのですが、しかし、IE8までなぜ発覚しなかったのか、というところを追究してみましたのでメモっておきます。


なお、この現象は、IE8ベータ版の頃にあった
IE8のwindow.openの挙動について
などのバグとは違います。


で、先に言い訳をしておくと、このコードを書いたのは大分前で、かつずっと問題なく(いや、あるんですが)動いていたので気がつきませんでした。

<?php
session_name("TEST_SID");
if(isset($_REQUEST["TEST_SID"])){
    session_id($_REQUEST["TEST_SID"]);
}
session_start();
if(session_id()){
    setcookie("TEST_SID", session_id(), time() + 1800,  "/");
}

if(!isset($_SESSION["count"])) {
    $count = 1;
    $_SESSION["count"] = 1;
    print "このページの訪問は1回目です!";
} else {
    $_SESSION["count"]++;
    print "このページの訪問は" . $_SESSION["count"] . "回目です!";
}
var_dump($_COOKIE);
?>

恐らく、
「セッションを継続するためにはセッションIDをセットしなければならない」
「セッションの有効期間を延ばすためにはクッキーの有効期間を延長すればよい」
という二つの勘違いから生まれたコードです。


この二つの勘違いのために、原因は、session_id関数とsetcookie関数で2つのSet-Cookieヘッダがブラウザに対して送られていて、IE8でセッションがぶつぶつ切れる挙動が発生していました。
session_id関数のマニュアルを読むと、

注意: セッション保持にクッキーを使用している場合、 session_id() において id 引数を指定すると、 カレントのセッション ID がセットされるものと まったく同一であるかどうかに関わらず、 session_start() が呼び出される際に常に新しいクッキーが送信されます。

とあります。なので、session_start()で1回、明示的にsetcookieを呼び出して1回、計2回クッキーが送信されることになります。

firebugで見てみると、

Set-Cookie	
 TEST_SID=9124aae2f8b09c6db00c887fa2173a1f; path=/ 
 TEST_SID=9124aae2f8b09c6db00c887fa2173a1f; expires=Fri, 28-Aug-2009 08:53:45 GMT; path=/

ってな感じです。


で、一つ目のクッキーは、expiresがなく、二つ目のクッキーはexpiresがあります。
これがミソで、expiresのないクッキーとあるクッキーが混在している時、IE8で次の現象が起こります。原因は分かりません。

IE8を全て閉じ、もう一度開いてスクリプトにアクセスした場合、問題はありません。そのまま、タブを一つ開いて、リロードを繰り返すと10回に1回くらいの割合でクッキーが削除されます。
タブを増やしていくと、2回に1回くらいの割合でクッキーが削除されます。

FireFox・Safari・IE6/7ではこの現象はおきません。


というわけで、上記のような勘違いだらけのプログラムを書く方はそういないとは思いますが、次のいずれかで解決することができます。

  • session_idをセットしない
  • setcookieを呼び出さない
  • session_set_cookie_paramsで、lifetimeをきちんとセットする。


はー疲れた。
そりゃ、現象についてどこにも情報がないわけだ。
というわけで、勉強しなおします・・・orz


追記:
んー。二つの勘違いといったけれど、二つ目は正しいのか。
但し、ユーザーがsession.gc_maxlifetime以上、リクエストしてこないとデフォルトでは他のユーザーのリクエストに対して1%の確率でGCが走ってセッションタイムアウトになると。
だから、Cookieの生存期間≒セッションの有効期間なわけか。

セッションが不意に切れちゃうことを防ぐためには、PHP と Web アプリケーションのセキュリティについてのメモ見たいなことをしなきゃいけない。

ってことは、なんとなくキリがよくて1800秒をセッションの有効期間と言っていたけれど、一番いいのはsession.gc_maxlifetime以下の値にすることか。


ふぅ。不勉強だ。


せんでん

1タップで写真共有tottepost
カメラのついたiPad/iPhoneで撮った写真を、その場でFacebook/Mixi/DropboxなどのサービスにアップロードできるtottepostというiOSアプリを開発しています!詳しくは、iTunes App Storeをご覧ください。


ご購入はこちら!
1タップで写真共有 - tottepost - ISHITOYA Kentaro