session_regenerate_id()でセッションが切れる

Read More

以前、Session Fixation攻撃対策としてセッションIDを書き換える方法を書いた。

■過去記事:[PHP]session_regenerate_id()と古いセッションファイルの削除方法
http://xirasaya.com/?m=detail&hid=216


この時書いた方法は2つで、一つはPHPのVerが5.1.0以降の時のsession_regenerate_id(true)という方法。

もう一つは自前で組む方法。


しかし今回、そのどちらを利用していてもセッションが切れる現象が起こることがわかった。


その現象が出たのが、リンクを踏んでページを読み込み中にすぐ別のリンクを踏んだとき。

これをすると結構な確立確率でセッションデータが消失する。
たぶんF5連打でも似たようなことになると思う。


おそらく連続してページにアクセスすることによって、
セッションの値を新しいセッションIDのものに入れる前に次のプロセスが走って空になるのではないかと予想する。


--
これに対応させるために以下の2つの対策をとった。
・セッションIDの書き換えの実行確率を入れる
・セッションIDの書き換えの間隔を空ける(一定時間経過させる)


そして先に結論から言うと、これ以降セッションが切れるという現象はピタリとなくなった




--
さて、実はセッションIDの書き換えの実行確立確率についてはこの問題が起こる以前よりやろうと思っていた。

それは、IDの書き換えは負荷が高いのだろう、
ページ遷移での画面読み込み時間が少しかかるようになってしまっていたからだ。

今回の問題を解決するのと同時に対応することとなってしまったので結果的には一石二鳥となった。


セッションIDは毎回毎回アクセスごとに書き換えてやらんでもいい。
ということで書き換えの実行確立確率を1/30とした。
ちなみにこの実行確立確率に明確な根拠はない。




次にセッションIDの書き換えの間隔を空けるというものだがこれは、指定時間内にページアクセスした場合はセッションIDの書き換え処理を行わないというもの。

これにはfilemtime()を利用した。

書き換え間隔は5分にした。
この時間にも特に根拠はない。



つまりこれでセッションIDの書き換えが行われる条件は、前回セッションIDを書き換えたときより5分以上経過していて、かつ30回に1回の割合でのみ実行されるというものになった。


実際に組むと以下のようになる。
if(mt_rand(1, 30)==1) {	// 実行確率
 $sess_file = 'sess_'.session_id();
 $sess_dir_path = ini_get('session.save_path');
 $sess_file_path = $sess_dir_path. '/'. $sess_file;
 $timestamp = filemtime($sess_file_path);
 $span = 5*60;		// 経過時間
 if(($timestamp+$span) < time()) {
  // PHP Ver取得
  $iPHPVer = (int)sprintf('%.3s', str_replace('.', '', PHP_VERSION));
  if($iPHPVer>=510) {
   session_regenerate_id(true);
  }else {
    $sess_tmp = $_SESSION;
    session_destroy();
    session_id(createUniqueKey(25, true));
    session_start();
    $_SESSION = $sess_tmp;
  }// end if
 }// end if
}// end if





しかし自前で組んだものでセッションが切れるのはまだわかるとしても、まさかPHP公式の関数session_regenerate_id(true)を使ってセッションが切れるようなことになるとは思わなんだ。


ふーやれやれ。



Comments(7)

1  名無し  2013/01/29 (火) 17:01 ID:XXXXXXXXX
なるほど
参考になります

2  名無しさん  2013/03/21 (木) 19:00 ID:GxC4vhb15
とても参考になりました。

僭越ですが...typo ?
「確立」->「確率」

3  シラサヤ  2013/03/21 (木) 21:02 ID:YqhslY615
>「確立」->「確率」
訂正しました。

言われるまでまったく気がついてなかった。
心からありがとう。



4  名無しさん  2013/07/03 (水) 11:44 ID:HxwNjwu15
PHPでなくRubyのWebrick::CGIでセッション管理機構を構築しようとしているものです。自前のregenerateでも同様の問題が起こっていたのですが、やはりPHPでも起こるんですね。解決方法、とても参考になりました。ログイン時にはアクセスするたびにIDを再生成していたのですが、その必要はないということですね。

5  シラサヤ  2013/07/03 (水) 20:56 ID:bVYbZu415
アクセス毎にSIDを再生成する必要はないと思います。
あくまでも「Sハイジャック予防」の意味合いの強い行為ですし。

つまり定期的にパスワードの変更を勧めてくるGoogleアカウントなどと変更理由は同じなわけです。
パスワードの変更は決定的なのっとり対策にはなりえないものの、長期間同じものを使い続けるよりリスクを下げることができます。
その意味を考えるとSIDもたまに変更すればいいわけで。

で、記事では再生成の実行確率を「適当」にしているというわけです。


また、SIDをDBやメモリで管理しているならともかくファイル(デフォルト)で管理しているの別の視点からも再生成の頻度は下げるべきでしょう。


あとSID再生成の話ではないですが、ボタンの連続押下によるデータの2重送信やリロードによるDBへの2重登録など、他にも色々と考えるべきところは多そうです。

まあ普通のシステムでそこまで大真面目にやる必要性は正直薄いんでしょうけど








6  たかやま  2013/11/20 (水) 17:23 ID:MtOW8Xp15
この記事で、今まで悩んでいたことが解決しそうです。
ありがとうございます。

ところで、

■過去記事:[PHP]session_regenerate_id()と古いセッションファイルの削除方法
http://xirasaya/?m=detail&hid=216

となっていますが、

http://xirasaya.com/?m=detail&hid=216

のタイポではないかと。

7  シラサヤ  2013/11/25 (月) 15:31 ID:5ZgLwAi15
たかやまさん リンクURLの間違い ご指摘ありがとう。
修正しました。

記事も参考になったみたいでよかったです。