[PHP]session_regenerate_id()と古いセッションファイルの削除方法

Read More

PHPのお話。


セッションハイジャック(Session Fixation攻撃)を防ぐための手段の一つとして「セッションIDを書き換える」という方法がある。

セッションIDを変えることがなぜ対策になるかは書きたい内容と離れるのでここでははしょる。


さて、セッションIDとはセッションファイルで管理されている。

セッションIDは一つのセッションファイルと対になっており、
もし利用中のセッションIDに対応するセッションファイルを物理削除するとそのセッションは切れてしまうこととなる。

このことから、
セッションID≒セッションファイル
と言ってよい。


ちなみにここでいうセッションファイルとは、
php.iniのsession.save_pathディレクティブに設定されているパス内に格納される(sess_から始まる)ファイルのことを指す。


前置きは終了。


--
さて、セッションハイジャック対策としてセッションIDを書き換えるにはsession_regenerate_id()を利用する。

■書き換え例(50点)
<?php
session_start();
session_regenerate_id();
?>

このままでは50点。


session_regenerate_id()を実行すると、以後は別のセッションIDを作って勝手にそちらを利用するようになる。


だが別のセッションIDを生成するとは、つまり内部的には別のセッションファイルを新たに生成することに他ならない。


この為上記のプログラムを実行するたびにファイルの数がどんどん増える。ガーベジコレクションが実行されるまで。



この問題を回避するためにPHPのVer5.1.0以降では第1引数を持たせることができるようになった。

■5.1.0以降でできる書き方
<?php
session_start();
session_regenerate_id(true);
?>

こうすると新たなセッションIDを作るのと古いセッションファイルを削除するのが一つのプロセスの中で完了する。

これなら何度実行してもそのせいでゴミが増える、ということを回避できるのでセッションハイジャック対策としては100点だと言えよう。



ただしこの方法、PHPのVerが5.1.0未満のものでは使えない。
session_regenerate_id()にこの第1引数が使えないからだ。

では5.1.0未満ではどうすればいいのか。

そう、5.1.0未満では自前でセッションファイルを削除してやらなければならないのだ。


さてセッションファイルの削除で注意しなければならないのはファイル削除の関数unlink()が使えないという点。

もう少し丁寧に言うと、プロセスの中で利用しているセッションファイルは、例え途中でsession_regenerate_id()を使って別のセッションIDを生成したとしてもプロセスが終わるまで古いセッションファイルにはロックがかかったままの状態が続く。
ゆえにunlink()では削除できないのだ。


このため同一プロセス中で物理削除するにはsession_destroy()という特別な関数を利用しなくてはならない。

■5.1.0未満の書き方
<?php
// セッション変数を一旦保存
$sess_tmp = $_SESSION;
// セッションファイルを破棄
session_destroy();
// セッションIDを再生成
session_id(createUniqueKey(25));
// 新しいセッションIDでセッションスタート
session_start();
// セッション変数を新しいセッションIDを使ったもので入れ直す
$_SESSION = $sess_tmp;
?>

createUniqueKey()は自作関数で25桁の半角英数字をランダムで生成する関数。今回は文字数の関係上はしょる。

-- 2013/01/30 追記 --
createUniqueKey()はdolemの独自関数my_uniqueid()として実装済。
■dolem:my_uniqueid()
http://dolem.jp/?m=builtin_function&category=&function=my_uniqueid



このとき注意しなくてはならないのはsession_destroy()をしただけでは新しいセッションIDに$_SESSIONの値は引き継がれない。
なので手動で$_SESSIONを移し変えてやる必要がある。



この方法は5.1.0未満でも5.1.0以降でも使える書き方なのでVerに囚われたくない人はこの処理を関数化して呼び出すようにしたらいい。

PHP_VERSION定数を使ってVerで処理を切り分けてもいい。



この古いVerでの書き方についてはあちこちのサイトで間違ったのが紹介され、かつそのまま増え続けていたのでいい加減ちゃんと動くのを書いてやろうと思っていた。



-- 2012/09/08 追記 --
PHPの公式な関数を使っていてもセッションIDが切れる現象が確認できた。これは大きな問題なのでぜひこちらも合わせて読んでもらいたい。
■記事:session_regenerate_id()でセッションが切れる
http://xirasaya.com/?m=detail&hid=221



Comments(1)

1  名無しさん  2013/02/09 (土) 04:01 ID:XXXXXXXXX
目から鱗です。
ありがとうございました。