CakePHPのセッションタイムアウトの指定はSession.timeoutだけではダメだった

CakePHP 2.xで、セッションタイムアウトの時間を指定しようとすると、下記のように書くと思います。

Config/core.php

Configure::write('Session', array(
'defaults' => 'php',
'timeout' => 1440, //1days ←単位は分
));

しかし、これだけではダメだったりするのです。
具体的には、1日経たなくてもセッションタイムアウトしてしまいます。

その理由は、PHPでのセッションファイルのガベージコレクションにあります。

PHPでは、セッションを開始すると(session_start())、ふつうはセッションファイルを作成します。
CentOS6.2にyumでPHPを入れた場合は、/var/lib/php/sessionあたりです。
CakePHPのcore.phpで、Session.defaultsを’cake’とした場合は、Appの/tmp/sessionになります。

このセッションファイルは、一定のタイミングでガベージコレクションの対象となり、削除されます。
その設定が、php.iniの下記の設定です。

session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440 ←単位は秒

gc_probabilityとgc_divisorは、ガベージコレクションを行う割合です。この例では、session_start()が呼び出されるうちの1/1000の割合でガベージコレクションが行われます。これは、session_start()が呼び出される度にガベージコレクションをやっていると負荷がかかるので、それを軽減する目的があるものと思います。

gc_maxlifetimeは、セッションファイルの最後の更新(そのセッションでのリクエストがあるとファイルが更新され、タイムスタンプが変わります)から、設定した秒数が経過したセッションファイルを削除するという指定です。
デフォルトでは1440秒なので、24分です。頻繁にアクセスがあって、1/1000の割合でのガベージコレクションも頻繁であったとすると、24〜25分でセッションファイルが削除されることになります。

さて、そうなると、CakePHP側で指定したSession.timeoutの値は何の意味があるのか?ということになります。

  1. セッションAがスタートし、放置
  2. セッションBがスタートし、頻繁にリクエスト
  3. セッションAが25分放置された後、セッションBがリクエスト
  4. ガベージコレクションの始動により、セッションAのセッションファイルを削除
  5. セッションAがリクエスト。CakeのSession.timeoutとは無関係に、そもそもセッションファイルが削除されているので、そのままセッションタイムアウト

つまり、gc_maxlifetimeには、CakePHPのSession.timeoutより長い時間が設定されていなければならないということです。

設定の方法は、php.iniで設定する方法だけでなく、CakePHPのcore.phpでも設定できます。

Config/core.php

Configure::write('Session', array(
'defaults' => 'php',
'timeout' => 1440, //1days ←単位は分
'ini' => array(
'session.gc_maxlifetime' => 86400, //1days ←単位は秒
),
));

これは思わぬ盲点でした。Session.timeoutの設定を行ったからといって、それで終わりではないのですね。

ついでに、もう一つ。
Session.timeoutの値には、どういう意味があるのか?ということです。

例えば、2012/1/1 10:00:00にCookieが出来た(Webサーバからブラウザに送り込まれた)とします。で、Session.timeoutが上記のように1440分だったとすると、その有効期限は2012/1/2 10:00:00になるわけです。
一度出来たCookieは基本的には再作成されませんし、有効期限が更新されることもありません。そのため、2012/1/2 10:00:01になったら、セッションタイムアウトすることになります。

有効期限を延長したい場合は、Cookieを再作成しなければなりません。CakePHPの設定では下記の方法で、自動的に再作成出来ます。

Config/core.php

Configure::write('Session', array(
'defaults' => 'php',
'timeout' => 1440, //1days
'autoRegenerate' => true, ←この行を追加
'ini' => array(
'session.gc_maxlifetime' => 86400, //1days
),
));

CakeBook2.xのセッションの説明にあるように、autoRegenerateをtrueにしてもリクエストごとに再作成されるわけではありません。

Session.autoRegenerate – このセッティングを有効にするとセッションとセッション ID の自動更新が有効になります。この値はリクエストの継続的な追跡にセッションの Config.countdown の値を使用します。カウントダウンが 0 になると、セッション ID が再生成されます。これはセキュリティ上の理由でセッション ID を頻繁に変更する場合に有用なオプションです。セッションの再生成が必要なリクエスト数のコントロールは、 CakeSession::$requestCountdown の変更によって可能です。

AppControllerのbeforeFilter等で下記のような設定を追加します。

function beforeFilter() {
CakeSession::$requestCountdown = 1;
}

「1」という設定が妥当かどうかはさておき、これでリクエスト毎にCookieが再作成されるので、最後のリクエストからSession.timeoutで設定した時間まで有効ということになります。

この記事を書いた人

井上 研一

株式会社ビビンコ代表取締役、ITエンジニア/経済産業省推進資格ITコーディネータ。AI・IoTに強いITコーディネータとして活動。2018年、株式会社ビビンコを北九州市に創業。IoTソリューションの開発・導入や、画像認識モデルを活用したアプリの開発などを行っている。近著に「使ってわかった AWSのAI」、「ワトソンで体感する人工知能」。日本全国でセミナー・研修講師としての登壇も多数。