複数Webサーバ、単一セッション管理時のsession_start()。
セッションIDがかぶるかもしれない問題用。

アドホック過ぎなのであってるかどうか心配です。

<?php
function session_start_uniq() {
    if (isset($_COOKIE[ini_get("session.name")])) {
        $sid = $_COOKIE[ini_get("session.name")];
    } elseif (ini_get("session.use_only_cookies") != 1 and isset($_GET[ini_get("session.name")])) {
        $sid = $_GET[ini_get("session.name")];
    } else {
        $server_addr = isset($_SERVER["SERVER_ADDR"]) ? $_SERVER["SERVER_ADDR"] : "";
        $sid = md5($server_addr.uniqid(rand(), true));
    }
    session_id($sid);
    session_start();
    return $sid;
}
?>

ref: PHPのセッションID4

引き続きセッションIDかぶっちゃう問題

p0t: PHPのセッションID3

一度session_id()を取得するんじゃなくて、

session_id(md5($_SERVER[‘SERVER_ADDR’].uniqid(rand(),1)));

とかやるのはどうですか?
某所でこんなコードを書いたことがあって、正解かどうか激しく気になります。正解でしょうか?教えてください。

あと、$_SERVERの値って、php.iniやらの設定で取れないことあるよね。そんな時どうしよう。どうしよう・・・

投稿者 kかべ : 2005年07月12日 14:16

kかべさんのこの案が素敵な感じがします。
調べてみるとセッションID発行md5(uniqid(””””,1))はほぼ等価っぽいので正解なのではないでしょうか!

php-4.3.11/ext/session/session.c

sprintf(buf, "%ld%ld%0.8f", tv.tv_sec, tv.tv_usec, php_combined_lcg(TSRMLS_C) * 10);
PHP_MD5Update(&context, buf, strlen(buf));

php-4.3.11/ext/standard/uniqid.c

if (more_entropy) {
    sprintf(uniqid, "%s%08x%05x%.8f", prefix, sec, usec, php_combined_lcg(TSRMLS_C) * 10);
} else {
    sprintf(uniqid, "%s%08x%05x", prefix, sec, usec);
}

$_SERVERはどういうとき使えないんでしたっけ。その場合は、どうしよう・・・。

ref: PHPのセッションID3

複数Webサーバ、単一セッション管理でセッションかぶっちゃう問題ですが、調べたら5人日ぐらいかかりそう・・・とか思ってましたが、この間の勉強会で聞いたら、

「session_id()でセッションID設定できる。」

一瞬で解決しました。

解決策をまとめると、

「セッション維持してくれるロードバランサーを使う」 OR 「セッションIDにアドレスをくっ付ける」

となりました。
実装はこんな感じか!?

session_id(str_replace(".", "a", $_SERVER["SERVER_ADDR"]).session_id());

Mojavi2で使うためにIPでユニークなセッションIDを使うコンテナ、IpUniqueSessionContainerを作ってみました。

<?php
require_once (USER_DIR."SessionContainer.class.php");

class IpUniqueSessionContainer extends SessionContainer {
    function load(& $authenticated, & $attributes, & $secure) {
        if (ini_get("session.auto_start") != 1) session_start();
        session_id(str_replace(".", "a", $_SERVER["SERVER_ADDR"]).session_id());
        if (!isset($_SESSION["attributes"])) {
            $authenticated = false;
            $attributes = array();
            $secure = array();
        } else {
            $authenticated = $_SESSION["authenticated"];
            $attributes = $_SESSION["attributes"];
            $secure = $_SESSION["secure"];
        }
        $_SESSION["authenticated"] =& $authenticated;
        $_SESSION["attributes"] =& $attributes;
        $_SESSION["secure"] =& $secure;
    }
}
?>

IpUniqueSessionContainer-0.1.zip

index.phpのUSER CONTAINER SETTINGSのところでこんな感じで使う。

require_once(BASE_DIR."opt/user/IpUniqueSessionContainer.class.php");
$user =& $controller->getUser();
$user->setContainer(new IpUniqueSessionContainer());

ref: PHPのセッションID2

PHPのセッションIDの続編。

やはり複数WebサーバでのセッションID重複の可能性があるというのが問題になってきた(気になってきた)のでまた調べてみたところ、前回の結論には一部間違いがありました。

msessionはセッション情報を全てメモリに保持しているのでセッションID のコリージョンを検出しています。

とありますが、ちょっと考えたらリクエスト終了する毎にプロセスが終了するPHPの仕組み上、複数のリクエストが作成するセッションIDを見張ることなんて不可能。

msessionとはPHP extentionの一つでMsession – Mohawk Session Managerというセッション管理サーバへのAPIなんですね。セッション管理サーバが常駐してセッションIDを見張るので重複することが無いんだそうです。

普通はmsessionを使っておけば問題なさそうです。
ただ、msessionサーバが落ちたら困るのでその辺どうするかが悩みです。

ref: PHPのセッションID

会社の五周年に楽天のKさんが来ていたのでPHP関連の質問を幾つかさせて頂きました。

Q1. PHPのセッションは重複するのか。(PHPのセッションID 参照)

A1. その理解の通り、1台では重複しないが複数台では重複する。

Q2. なぜPHPをApache2のようなマルチスレッドサーバで動かしてもおかしくならないのか。

A2. マルチスレッドでは動かない。Apache2で動かす場合はプロセスベースで動く設定にする。

その他にも楽天のDB構成やPythonの話などいろいろと聞かせてもらって勉強になりました。疑問がズバーン解決してとてもすっきりしました。

先日、PHPのセッション絡みの障害があり、調べてみました。何かというと「セッションIDが重複することがあるのか」ということについて。

PHP4.3.9のソースで見てみるとextというディレクトリがあります。これは拡張機能関連のソースがある場所のようで、pgsqlやmingやxmlなどが入ってます。ここにsessionディレクトリもありました。。拡張といっても本当のコア機能以外は全部ここに入っているようです。

php-4.3.9/ext/session の中のsession.cを見てみるとphp_session_create_idという露骨な名前の関数が定義されています。中身は大雑把に見ると、時間と php_combined_lcg()をMD5でハッシュ化しているようです。php_combined_lcgをググってみると、

[PHP-users 11996] Re: 「他人の情報を表示するサイト」

php_combined_lcgはマルチプロセスサーバー(プロセスをフォームするサーバー) ではPIDをマルチスレッドサーバーではthread_idを利用して疑似乱数を生成してい ます。したがって、仮に、micro秒単位で同じ時刻にアクセスしても同じセッション IDは生成されません。

msessionはセッション情報を全てメモリに保持しているのでセッションID のコリージョンを検出しています

どうやらPHPのセッションIDは理論的に絶対に重複しないようです。でもこれって、Webサーバが複数ある状態で1つのDBにセッションを保存している場合、同じ時刻で偶然プロセスIDも一緒だったら重複しちゃうんじゃないでしょうか。もう少し調べてみたいです。

2005/05/17 追記: 一部間違いがありました。 ref: PHPのセッションID2