************************************************* セッションの切断に関するメモ − いわしたゆうじ 2003/11/15 作成 2003/11/17 session.gc_maxlifetime とsession.gc_divisor の 記述間違いを訂正。 2010/04/21 検証可能なセションIDを使って セッションハイジャックの可能性を減らす試みを追記。 ************************************************* URLにセッションIDが付加されている場合、 数十分経過後にあらためてお気に入りからアクセスしても、 前回のセッションが継続されたままになっている。 ※クッキーでセッションIDの受け渡しをしている場合は、 session.cookie_lifetime = 0 に設定することで、 ブラウザを閉じると同時にクッキーの有効期限が切れ、 結果的にセッションが切断されるが、 URLでセッションIDの受け渡しを行う場合は、 実際にセッションIDが破棄されていない限り、セッションは 継続されたままである。 ************************************************* ユーザー認証に限って言えば ************************************************* これは仕様なので当然の現象なのだが、 ユーザー認証に限って言えば、クッキー『有効・無効』の違いで、 『ブラウザを閉じる≒ログアウトする』 これが保たれなくなるのは、望ましくない。 また、古いセッションIDがいつまでも、セッションを維持し続けるのは セキュリティー面からも、良いこととはいいがたい。 クッキーが有効・無効に関わらず、不要となったセッションを 速やかに切断することが出来ないものか、設定やコードを検証してみる。 ************************************************* 関連するセッション初期設定(おさらい) ************************************************* # セッションデータを保存しておくディレクトリ。 # デフォルトで、/tmp # このディレクトリ下に、セッションごとのファイルが作成される。 # セッションIDが破棄の対象となるタイミングは、 # 各ファイルのタイムスタンプが、現在時刻よりも、session.gc_divisor で指定した # 時間以上に古いものとなった時点。 # 但し、破棄の対象となるだけで、実際に破棄(削除)されるタイミングは、 # ガーベジコレクション(ゴミ処理)が実行された時。 # これは、session.gc_probability で指定した確率で実行される。 session.save_path = /tmp # セッションが使用するクッキーの有効期限を指定する。 # デフォルトで、0(単位:秒) # ブラウザを閉じるとクッキーは無効となる。 session.cookie_lifetime = 0 # セッションIDを保存する際に、クッキーを使用するかどうかを指定する。 # (1:使用する 0:使用しない)デフォルトで、1 session.use_cookies = 1 # ガーベージコレクションにより、無効となったセッションIDの破棄が # 行なわれる確率を指定する。 # デフォルトで、1/100 # session_start()のコール100回につき1回、 # 無効となったセッションIDの破棄が行なわれる。 # 1/1に設定すれば、session_start()コールの度に、無効となった # セションIDの破棄が行なわれるようになる。 session.gc_probability = 1 session.gc_divisor = 100 # セッションIDが破棄の対象となるまでの時間を指定する。 # デフォルトは、1440(単位:秒) # 最終のアクセスから1440秒を過ぎたものが破棄の対象となる。 session.gc_maxlifetime = 1440 # URLを示すタグに、セッションIDを付加をするかどうかを指定する。 # 0:付加しない 1:付加する(デフォルトは、0) # session.use_cookies = 1 の場合、クッキーが利用可能ならば付加されない。 # クッキーが利用不可ならば、自動的にセッションIDがURLに付加される。 session.use_trans_sid = 0 ************************************************* 考え中(1) ************************************************* あらためて php.ini のセッション部分をまとめてみると、 クッキーが有効・無効に関わらず、不要となったセッションを速やかにやかに 切断するためには、セッションIDそのものを破棄すれば良いと考えられる。 では、セッションIDを破棄するにはどうしたらよいか? ************************************************* セッションIDを破棄する条件 ************************************************* 実際にセッションIDが破棄されるのは、次の条件が整った時。 1.セッションIDが破棄の対象となる。 2.ガーベージコレクションにより、セッションIDの破棄が実行される。 ************************************************* 考え中(2) ************************************************* 1.セッションIDが破棄の対象となるのは、最後のアクセスから session.gc_divisor で指定した時間を経過した時点なので、 この値を短く設定すればよいのではないか?。 2.session_regenerate_id() を使って、有効なセッションIDを 次々と変更することで、古いセッションIDを意図的に、 破棄の対象とすることが出来るのではないか? 3.セッションIDの破棄が実行されるのは session.gc_probability で 指定した確率なので、セッションIDの破棄が、頻繁に行なわれるよう、 この確率を高く設定すればよいのではないか? これらを確かめるために、次のような設定とテストコードを準備した。 ************************************************* php.ini の設定 ************************************************* # セッションIDの破棄が頻繁に行われるよう100%に設定 session.gc_probability = 100 session.gc_divisor = 100 # 早く破棄の対象になるよう60秒に設定 session.gc_maxlifetime = 60 # クッキーが無効の場合自動的に、 # URLにセッションIDが付されるよう1に設定 session.use_trans_sid = 1 ************************************************* テストコード session_test.php ************************************************* ログイン中

\n" } else { echo "

ログイン未

\n" } ?>
ID:

************************************************* 検証 ************************************************* IEとOperaでそれぞれ4つずつブラウザを開いて、クッキーが有効なものと 無効なものを混ぜた状態で、お気に入りからアクセスしたり、 ブラウザを開きなおしたりしながら、上記コードにアクセスしてみました。 確かに、セッションIDの破棄が頻繁に行われるようになり、 古いセッションIDが付加されたURLからのアクセスは、 ログオン未とすることができました。 ※但し、session.gc_maxlifetime = 60 だと、1分でページが期限切れとなるので、 ブラウザを閉じなくても、ログアウトしてしまう。 この値を小さくしすぎると、実用的ではなくなるので、 デフォルトの1440あたり(24分)が適切なのかもしれない。 ※最後にアクセスのあったブラウザのセッションIDだけは、破棄することが出来ない。 これは、新たなセッションの開始が、セッション破棄のタイミングになので、 しかたがない事かもしれない。 これを解決するには、CGIやCLI版PHPなどの外部プログラムの力を借りて、 定期的にセッションデータを直接破棄する仕組みが必要なようだ。 PHPの領域を超えるので今回は保留。機会があればチャレンジすることにする。 ※session_regenerate_id() によるセッションIDの変更は、 現在アクティブなセッションデータファイルのファイル名を変更するのではなく、 新たなファイルを作ることで実現されている。 session_regenerate_id() がコールされる度に、新規ファイル作成と データ書き出しが行われるので、サーバーにかける負担が気になる。 クッキーが無効の場合のみ、session_regenerate_id() をコールするよう 変更した方がよさそう。 実際の運用状態なら、特定のページにだけ session_regenerate_id() を 記述しておくのも良い方法かもしれない。 ※session.gc_probability、session.gc_divisorについても、 100%に設定すると、アクセスがある度にスクリプトの実行とは 関係ない部分でサーバーに負担をかけることになる。また、 常に破棄が実行されるならば、ガーベージコレクションは 意味を持たなくなる。 よって、アクセス統計などを元に、適切な値を導き出す必要がある。 具体的には、常時100人のユーザーがアクセスしているようなサイトを、 デフォルトの1/100とし、これ未満のサイトならば、 1 / 一人当たりの平均参照ページ数 となるよう設定するすればよいと考えられる。 反対に常時100人以上のユーザーがアクセスするようなサイトであれば、 1 / ユーザー数 に設定することで、サーバーへの余分な負担を軽減できる。 (新たなユーザーが参加するタイミングで、セッションIDが 破棄されることを期待した値です。) ************************************************* 結論 ************************************************* PHPデフォルトのセッションIDを破棄する仕組みでは、 セッションIDの寿命を、完全に制御することは出来ない。 これを実現するには、ページへのアクセスが実行命令である DSO版PHPでは無理。 CGIやCLI版PHPなどを使い、無効となったセッションIDを破棄する、独自の実装が必要。 最後になりましたが、完全ではないものの、 DSO版PHPだけで、ある程度実現できる方法をまとめておきます。 ============================== php.ini ============================== session.cookie_lifetime = 0 session.use_cookies = 1 ;1/平均参照ページ数(例:一人平均5ページならば、1/5 に設定) session.gc_probability = 1 session.gc_divisor = 5 session.gc_maxlifetime = 1440 session.use_trans_sid = 1 ============================== スクリプト ==============================