Hatena::ブログ(Diary)

プログラマはサイコロを振らない このページをアンテナに追加 RSSフィード

2008-07-26 (Sat)

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

| 14:21 | 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

Dr_RadialistDr_Radialist 2011/06/26 11:58 大変参考になりました。ありがとうございます。

sigeomisigeomi 2011/08/14 23:37 session_start();を忘れてますよ。

あすりんあすりん 2013/05/14 18:34 スポーツクラブの例えがとてもわかりやすく
すんなり理解することが出来ました!!

To_aru_UserTo_aru_User 2014/02/26 11:44 この記事はsession_regenerate_id関数の実行を前提としていないので少々大袈裟なところがあります。実際は「スポーツクラブの所持する会員の情報を破棄する」「会員カードを破棄する」「その人の会員番号を無効なものとする」の最低どれか1つを行うだけで安全性は保証されます。変数の一貫性を保ちたいならば1番目、ディスク領域を無駄に圧迫するのが嫌であれば2番目を実行すると良いでしょう。3番目は必要ありません。詳しくはリンク先で。

To_aru_UserTo_aru_User 2014/02/26 11:50 この記事はsession_regenerate_id関数の実行を前提としていないので少々大袈裟なところがあります。実際は「スポーツクラブの所持する会員の情報を破棄する」「会員カードを破棄する」「その人の会員番号を無効なものとする」の最低どれか1つを行うだけで安全性は保証されます。変数の一貫性を保ちたいならば1番目、ディスク領域を無駄に圧迫するのが嫌であれば3番目を実行すると良いでしょう。2番目は必要ありません。詳しくはリンク先で。

To_aru_UserTo_aru_User 2014/02/26 11:55 (順番間違えてたから訂正したけど古いコメント削除できない)

あと「会員カードを破棄する」の例は正しく形容出来ているのですが、残りの2つの喩に少し語弊があるかもしれません。どちらも情報の破棄を行いますが、「メモリ上にある情報を破棄する」「ディスク上にある情報を破棄する」といった明確な違いが示せていません。

yy 2015/08/06 16:15 $_SESSION = array(); をせず、setcookie(session_name()〜と session_destroy();だけ行うとどうなるのでしょうか?

yy 2015/08/06 16:15 $_SESSION = array(); をせず、setcookie(session_name()〜と session_destroy();だけ行うとどうなるのでしょうか?

トラックバック - http://d.hatena.ne.jp/Kappuccino/20080726/1217049706