PHP
FuelPHP
elasticache

FuelPHPでセッション共有時の注意点

More than 3 years have passed since last update.

TL;DR

FuelPHPでセッション情報をMemcacheやRDBMSなどを使って複数ノードで共有する場合には、

  • app/config/crypt.php

を複数ノードで同じ値となるように設定する必要がある。

背景

Webアプリを複数ノードで構成して、ロードバランサでセッション維持をしない場合には、どのノードにユーザの再アクセスがあってもセッションを維持するために、セッションストアとしてMemcacheやRDBMSなどを使うと思います。
例えば、以下のような構成。
AWS Design.png

このような構成を FuelPHP で行う場合の注意点を書いておきます。
図と、以降の説明では ElastiCache(Redis)を使っていますが、注意点についてはRDBMSだろうが普通のMemcacheだろうが同じです。

セッションストアの指定

FuelPHP では、セッションストアの指定を app/config/session.php でまず指定します。(なお、環境に応じて設定を変更したい場合は、 config/staging/session.php などに記述すればOK)

Redisを使うのであれば、session.phpの記述は以下のようになります。

session.php
<?php

return array(
    'driver' => 'redis',
    'redis' => array(
        'cookie_name' => 'fuelrid',
        'database' => 'default',
    ),
);

さらにRedisの場合は、db.phpに接続先の情報を記述します。

db.php
<?php

return array(
        'redis' => array(
            'default' => array(
                'hostname' => 'xxxxxx.yyyyyy.nnnn.apne1.cache.amazonaws.com',
                'port' => 6379,
                'timeout' => null,
            )
        ),
);

セッションストアとして他のシステムを使う場合はなどは、下記公式ドキュメントを参照するとよいです。

暗号キー設定

で、ここからが重要で、これだけの設定だけでは、本質的にはセッションの共有ができません。
ロードバランサが別ノードに新たにリクエストを振るたびに、セッションが維持できないという症状がでます。(環境の作り方次第では、下記情報が同じになっているので、意図せず正しくは動作することもあります)

これは、FuelPHP ではセッションIDをCookie名に設定する時に暗号化を行っているのですが、この時に使用する暗号キーがノード毎に異なることが原因です。
この暗号キーはデフォルトでは設定されておらず、必要となったときに初めて app/config/crypt.php に自動的に生成されます。

ファイルの内容は以下のようなものです。

php.crypt.php
<?php
return array(
  'crypto_key' => '9eEBP8okkcz4xAo9rU5h4f7Q',
  'crypto_iv' => 'HncqRUWjEl2Y53sqawnK4Y7Q',
  'crypto_hmac' => 'ra4OxkVxYthofVMlAk0ncQrQ',
);

この内容を複数のノードで同じとなるようにします。

crypto_key, crypto_iv, crypto_hamc の3つが必要となり、それぞれ以下の条件を満たす必要があります。そうでなければ、再作成されてしまいノード毎に異なることになってしまします。

  • base64デコード可能な文字列
  • 文字列の長さは4の倍数

core/classes/crypt.php の _init() メソッドを見るとわかると思います。

そこからの抜粋で下記のようなコードで暗号キーに必要な値を生成できます。

<?php

function safe_b64encode($value)
{
    $data = base64_encode($value);
    $data = str_replace(array('+','/','='), array('-','_',''), $data);
    return $data;
}


$crypto = '';
for ($i = 0; $i < 8; $i++)
{
    $crypto .= safe_b64encode(pack('n', mt_rand(0, 0xFFFF)));
}

echo $crypto . "\n";

参考