スレッド表示 | フラット表示〕 全トピック 920 件中 32 番目 次≫ ≪前

セッション情報が上書きされる

created: 2009-03-02 18:33 | modified: 2009-03-04 00:52 | reply: 4

[4264] セッション情報が上書きされる

user: pyon | created: 2009-03-02 18:33
現在開発しているサイトにて下記の現象が発生しています。
セッション情報はDBに保存しております。

1:セッション開始
2:ログイン認証されていない場合(セッション情報にユーザー情報がない場合。もしくはユーザーアカウントがない場合)は強制的にログイン画面表示
3:ログイン画面表示
4:ログイン認証
5:認証OKの場合セッションにユーザー情報を格納してリダイレクト
6:認証されている(セッション情報にユーザー情報が格納されている)はずなのでトップページを表示する・・・はずがセッション情報が初期化されている

5の段階でセッション情報(DB)を確認すると確かにユーザー情報が格納されているんですが、その後なぜか空で上書きされているようです。


セッション情報をDBに保存するための独自クラス内で確認しましたが、どうもリダイレクトの後に上書きされてしまっているようで、debug_backtrace()で書き込みしようとしているファイルを特定してみましたが呼び出し元は空でした・・・


「807」のトピック(http://www.sound-uz.jp/php/bbs/thread/807)で上書きされるという現象が認知されているようなんですが、どうやって回避したんでしょうか?(というかそもそも原因は何でしょうか?)

ちなみに、セッション情報をファイルに保存すると上記現象は発生しません。セッション保存用ハンドラがおかしいのかとも思いましたが、普通にセッション変数に値を入れたりした場合は正常に動いており、リダイレクトなどをすると初期化されてしまうようです。
PHPは5.2.6です。
reply: 4265 返信 編集 削除

[4265] Re.セッション情報が上書きされる

user: ゆうじ | created: 2009-03-03 00:47
こんばんわ。

Win2000SP4、Apache2.0.63、PHP5.2.9、Mysql4.1.22 環境で
自作のセッションハンドラで再現するか試してみました。

sess1.php をリクエストすると
$_SESSION['test']を'テスト文字列'で初期化して
sess2.php にリダイレクトします。
sess2.php で $_SESSION['test'] を表示するものです。

このコードだと sess2.php で 'テスト文字列' が表示されてるので
リダイレクトそのものが原因とは考えにくいです。

セッションが保存されるタイミングに問題があるかもしれません。
リダイレクトの前に session_write_close() を
呼んでみるとどうでしょうか。

現象を再現できる最小限のコードを挙げてもらえれば
こちらでも試してみます。


mySession.php
<?php
/**
* Table `mysess`
*
CREATE TABLE `mysess` (
`sessid` varchar(50) NOT NULL default '',
`value` blob,
`time` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`sessid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
*/

/**
* mySession: Session handler
*/
class mySession
{
protected $db = null;
public $tableName = 'mysess';
public $keyName = 'sessid';

function __construct ()
{
$this->db = mysql_connect('localhost', 'username', 'password');
if (mysql_error() || !is_resource($this->db))
return;
mysql_select_db('mydbname', $this->db);
if (mysql_error($this->db))
return;
}

function open ( $save_path, $session_name )
{
if (is_resource($this->db))
return true;
else
return false;
}

function close ()
{
return true;
}

function read ( $sessid )
{
$lifetime = time() - 1800;
$sql = "select * from `".$this->tableName."`where `".$this->keyName."` = '".mysql_escape_string($sessid)."' and `time` > ".$lifetime;
$results = mysql_query($sql, $this->db);
if (mysql_error($this->db) || !is_resource($results) || !mysql_num_rows($results))
return '';
$record = mysql_fetch_assoc($results);
if (!isset($record['value']) || !strlen($record['value']))
return '';
return $record['value'];
}

function write ( $sessid, $value )
{
$time = time();
$sql = "replace into `".$this->tableName."` set `".$this->keyName."` = '".mysql_escape_string($sessid)."', `value` = '".mysql_escape_string($value)."', `time` = ".$time;
mysql_query($sql, $this->db);
if (mysql_error($this->db))
return false;
else
return true;
}

function destroy ( $sessid )
{
$sql = "delete from `".$this->tableName."` where `".$this->keyName."` = '".mysql_escape_string($sessid)."'";
mysql_query($sql, $this->db);
if (mysql_error($this->db))
return false;
else
return true;
}

function gc ( $maxlifetime )
{
$lifetime = time() - $maxlifetime;
$sql = "delete from `".$this->tableName."` where `time` < ".$lifetime;
mysql_query($sql, $this->db);
if (mysql_error($this->db))
return false;
else {
mysql_query("optimize table `".$this->table_name."`", $this->db);
return true;
}
}
}
?>


sess1.php
<?php
require_once 'mySession.php';

$sessHandler = new mySession;
session_set_save_handler (
array(&$sessHandler, 'open'),
array(&$sessHandler, 'close'),
array(&$sessHandler, 'read'),
array(&$sessHandler, 'write'),
array(&$sessHandler, 'destroy'),
array(&$sessHandler, 'gc')
);

session_start();

$_SESSION['test'] = 'テスト文字列';
header('Location: http://localhost/sess2.php');
exit();

?>


sess2.php
<?php
require_once 'mySession.php';

$sessHandler = new mySession;
session_set_save_handler (
array(&$sessHandler, 'open'),
array(&$sessHandler, 'close'),
array(&$sessHandler, 'read'),
array(&$sessHandler, 'write'),
array(&$sessHandler, 'destroy'),
array(&$sessHandler, 'gc')
);

session_start();

if (array_key_exists('test', $_SESSION)) {
echo $_SESSION['test'];
} else {
echo 'Empty session';
}
exit();

?>
Parent: 4264  reply: 4266 返信 編集 削除

[4266] Re.セッション情報が上書きされる

user: pyon | created: 2009-03-03 10:40
説明不足で申し訳ありません。
実は、ローカルの環境と開発環境がありまして、ローカル環境(WindowsXP+Apache2.2.8+PHP5.2.8+MySQL5.1.3)では問題なく動きますが、開発環境(CentOS5.2+Apache2.2.8+PHP5.2.6+MySQL5.1.3)ではセッション情報が上書きされてしまいます。
セッションハンドラ関数の「write」のタイミングで書き込んでいる情報をファイルに落としてみたんですが、スクリプト終了時(もしくはリダイレクト後のスクリプト開始時かセッション開始時?)に、セッション情報が再度書き込まれています。
これはローカル環境でも同様ですが、ローカルの場合は正しいセッション情報が書き込まれるのに対し、開発環境では空で書き込まれているようです。

また、開発はZendFrameworkを使っていますが、ZendFrameworkを使わないでセッション管理した場合も同じ動きをしています。
リダイレクト時、スクリプト終了時に「session_write_close」は行っています。
確実に再現するコードがあるわけではないので環境の問題かとは思うんですが、既知のPHPのバグ(?)のようなものがあるかと思い質問させていただきました。

実際のコードは、コメントいただいているコードとほぼ同様な処理です。

セッションをDBに保存せずにファイルシステムを使う場合は現象が発生しないので、そのあたりが関係しているかと思うんですが・・・
Parent: 4265  reply: 4267 4268 返信 編集 削除

[4267] Re.セッション情報が上書きされる

user: ゆうじ | created: 2009-03-03 19:47
前記の環境のPHPを5.2.6にすげ替えて
同じコードを試してみましたが
sess2.php で "テスト文字列" が表示されたので
PHP5.2.6の問題ではないと思われます。

原因の絞込み難しいですね。
前記のテストコードを問題の出てる環境で試してみれば
少なくとも、環境(設定)の問題か、スクリプトの問題かを
切り分け出来る気がします。

#追記
すでに確かめられてるかもしれませんが
エラーログに何か残ってないでしょうか。
error_reporting = E_ALL でエラーメッセージも
出てないのでしょうか。
Parent: 4266  返信 編集 削除

[4268] セッションの上書きを再現出来るパターン

user: ゆうじ | created: 2009-03-04 00:52
ひとつ思い出したことがあります。

例えば以下の sess3.php の中の prototype.js が存在しないと仮定します。
prototype.js が無ければあらかじめ設定してある404エラーページが返ります。

sess3.php
<?php 
session_start();
?>
<html>
<head>
<title>Session Test</title>
<script type="text/javascript" src="/js/prototype.js"></script>
</head>
<body>
<p>
<?php
if (isset($_SESSION['test']))
echo $_SESSION['test'];
else
echo 'Empty Session';
?>
</p>
</body>
</html>

404エラーページが呼ばれるのは
sess3.php の処理が完全に終了した後なので、
この時の404エラーページがPHPスクリプトで、且つ
セッションを使っていた場合、再びセッションが復帰されます。

これ以降の処理でセッションの値を参照しようとしても
404エラーページのセッション処理で上書きされてる現象がおこります。


また、mod_rewrite を使ってこんな具合に
存在しないファイルのリクエストを全てindex.php に処理させてる場合、

.htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php$1 [L]
index.php で出力したHTMLに、存在しない画像・CSS・Javascriptなどの
リクエストが含まれてれば、index.php が何度も呼ばれ、
その度にセッションが上書きされます。
最近はこっちのほうがありがちですね。


リダイレクトともセッションハンドラとも結びつきませんが
サーバー引越しの時にこれにハマったのを思い出しました。
ミラーリングしてたつもりが完全には出来てなかったわけです。
「セッションが上書きされる」で思いあたるのはこのくらいです。

また「セッションが消える」で思いつくのは
register_globals = On ですが PHP5では考えにくいですね。
【参考】[4103] セッションが消えてしまいます。
http://www.sound-uz.jp/php/bbs/thread/4103?view=flat


クッキーの値やステータスコード404が返ってないかなど
一度クライアントからデバッグしてみるといいかもしれません。
私は Firebug の 接続(ネットワークモニター)を使って検証しました。
Parent: 4266  返信 編集 削除
スレッド表示 | フラット表示〕 全トピック 920 件中 32 番目 次≫ ≪前
ページの一番上へ
Googleグックマークに登録 Yahooグックマークに登録 livedoorクリップに登録 @niftyクリップに登録 はてなブックマークに登録 deliciousに登録 Buzzurlに登録 FC2ブックマークに登録
最近更新された掲示板トピックス
管理人Blog
Yahoo Search

最近更新したNote
PHPマニュアル
今日のブックマーク
PHPマニュアル関数検索
関数名を入力し検索ボタンをクリック↑