PHPでセッションを完全に破棄する方法

PHPでセッションを破棄する方法について、きちんと解説されたものが見つからなかったので書いておく。


まず、PHPでセッションを破棄する方法自体はPHPのマニュアルの載っている。↓の部分だ。

<?php
// セッション変数を全て解除する
$_SESSION = array();

// セッションを切断するにはセッションクッキーも削除する。
// Note: セッション情報だけでなくセッションを破壊する。
if (isset($_COOKIE[session_name()])) {
    setcookie(session_name(), '', time()-42000, '/');
}

// 最終的に、セッションを破壊する
session_destroy();
?>


問題は、このコードについてまともな説明がされていないことだ。よくわからないままに使っている人も多いように思える。例えば「PHP セッション 破棄」ではてなダイアリーを検索すると、次のような記事がヒットする。

これでセッションを破棄できるみたい。
$_SESSION = array();でブラウザ内で持っているパラメタを消して、
session_destroy();でサーバ内の/tmpとかに保存されてる実ファイルを消すといったイメージだろうか

http://d.hatena.ne.jp/purazumakoi/20071017/1192613461

もしかして
$_SESSION = array();
が正解???

まあ、session_destroyすればいいだけの話なんだけどさ。。。

http://d.hatena.ne.jp/kidd-number5/20070312/1173691586

これらは大きな間違いを犯してしまっている。



セッションの仕組みをスポーツクラブの会員に例えてみよう。会員はクライアント、スポーツクラブはサーバだ。スポーツクラブは会員に会員番号与え、その会員番号の書かれたカードを発行する。スポーツクラブ側では、その会員についての情報(名前、年齢、電話番号など)を、会員番号に関連付けて保存している。

この場合、会員番号がセッションID、会員カードがブラウザに記録されているクッキー、スポーツクラブが所持している会員についての情報がセッション変数ということになる。

もしこのスポーツクラブを辞めることになったら、

  1. スポーツクラブの所持する会員の情報を破棄する
  2. 会員カードを破棄する
  3. その人の会員番号を無効なものとする
という三つの処理をすることで、完全に情報を破棄することができる。

もし三つのすべてを行わずに、例えば2だけを行った状況を考えてみると、その人のカードを偽造した別の誰かがやってきたら、スポーツクラブ側は本人だと思って受け入れてしまうだろう(セッションで考えると、これはセッションハイジャックという攻撃になる)。


では、このスポーツクラブの例を交えながら処理の中身を見てみよう。



まず初めに、

<?php
// セッション変数を全て解除する
$_SESSION = array();
?>

についてだが、これはセッション変数の破棄を行っている。これは、「スポーツクラブの所持する会員の情報を破棄する」に対応する。

PHPではセッション変数は連想配列の形で保持されている。array()は空の配列を生成するので、セッション変数を表す連想配列$_SESSIONは初期化されたことになる。参照をなくした元々の$_SESSIONの中身は、ガベージコレクションによって解放される。



次に、

<?php
// セッションを切断するにはセッションクッキーも削除する。
// Note: セッション情報だけでなくセッションを破壊する。
if (isset($_COOKIE[session_name()])) {
    setcookie(session_name(), '', time()-42000, '/');
}
?>

についてだが、これはクライアントのブラウザにクッキーとして記録されているセッションIDの破棄を行っている。これは「会員カードを破棄する」に対応する。

セッションIDはクッキーで管理する場合と、POSTパラメータなどで管理する場合があるが、クッキーで管理する場合はそれを破棄しないと、クライアントのブラウザにセッションIDが残ったままになってしまう。

session_name()は、セッションID名を返す関数だ(初期状態では、セッションID名は'PHPSESSID'という文字列になっている。つまり、session_name()の戻り値は'PHPSESSID'となる)。クッキーには、PHPSESSID=1ab6309dのような形で値が記録されている。この場合、1ab6309dがセッションIDだ。サーバはこのセッションIDによって、誰からアクセスされたのかを判別している(この仕組みをセッションという)。

つまりここでやりたいのは、ブラウザのクッキーに記録されているPHPSESSID=1ab6309dを破棄するということだ。最初のif文は、PHPSESSIDという名前でクッキーが記録されているかを調べ(セッション管理にクッキーを用いていない場合はここでfalseとなり、ifの中に入らない)、ifの中に入った場合はPHPSESSIDというクッキーを破棄する。


setcookie()はクッキー変数を設定する関数だ。

一つ目の引数は、クッキー変数名(今はセッションIDを表すPHPSESSID)だ。


二つ目の引数は、そのクッキー変数に設定する値だ。これはどうせ破棄する変数なので何でもいい。何でもいいので''になっている。


三つ目のtime()-42000はクッキーの期限を設定している。ここで設定した時刻になればクッキー変数をブラウザが破棄してくれる。値は、1970年1月1日0時0分0秒(この時刻をエポックという)からの経過秒数で指定することになっている。

time()はエポックからの経過秒数を返す関数だ。もしここにtime()を入れれば、期限は今この瞬間、ということになる。これでも即座にクッキーが破棄されるだろうが、安全のために42000秒前に設定しているのだろう。別にtime()-1000だろうが、time()-100000だろうが同じように動作すると思われる。


四つ目の引数はあまり気にする必要はないかもしれない。もしwww.example.comというドメインのサイトを所持しているとして、そのドメイン下の全体に対してクッキーを設定するには'/'と設定すればいい。www.example.com/test/というディレクトリ下にだけクッキーを有効としたい場合は'/test/'と設定する。セッションをサイト全体で管理しているなら'/'としておけばいい。



最後に、

<?php
// 最終的に、セッションを破壊する
session_destroy();
?>

だが、これはサーバ側での、セッションIDの破棄だ。これは「その人の会員番号を無効なものとする」に対応する。

サーバは与えられたセッションIDに対応したセッション変数を読み込み、$_SESSIONとして利用できるようにする。このsession_destroyを行わなければ、たとえセッション変数を破棄し、セッションIDのクッキーを破壊したとしても、別の誰かが1ab6309dというIDでアクセスしてきたならば、その人になりすませるということだ。

session_destroy()を行うことで、もし誰かが1ab6309dでアクセスしてきても、そんなIDの人はいませんよ、ということになる。



なお、session_unset()を使うように説明しているページが多く見つかるが、現在の$_SESSIONを用いる書き方をしている場合、session_unset()を使ってはいけない

また、unset($_SESSION)などもしてはいけない。これをすると、セッション変数を格納する連想配列が消えてしまうことになり、セッション変数を登録できなくなってしまう。

セッション変数全体ではなく、あるセッション変数を消去したい場合にのみ、unset($_SESSION['セッション変数名'])を用いる。

質問サイトの良回答にすら、このような間違いがあるので注意が必要だ。

$_SESSIONそのものを消し去ってしまいたい場合は、
unset($_SESSION);
の代わりに
session_unset();
をコールする必要があります。

http://oshiete1.goo.ne.jp/qa3224862.html

このようなことをしてはいけない




※セッションの仕組みについては次のページが参考になる
http://c-brains.jp/blog/wsg/08/05/22-193020.php