2 フォロワー

セッションとクッキー

セッションとクッキーを使用すると、複数のユーザーリクエストにわたってデータを永続化できます。プレーンなPHPでは、それぞれグローバル変数`$_SESSION`と`$_COOKIE`を通してアクセスできます。Yiiはセッションとクッキーをオブジェクトとしてカプセル化し、オブジェクト指向の方法でアクセスできるようにし、さらに便利な機能強化を提供します。

セッション

リクエストレスポンスと同様に、デフォルトではyii\web\Sessionのインスタンスであるアプリケーションコンポーネントの`session`を通してセッションにアクセスできます。

セッションの開始と終了

セッションを開始および終了するには、次の操作を行います。

$session = Yii::$app->session;

// check if a session is already open
if ($session->isActive) ...

// open a session
$session->open();

// close a session
$session->close();

// destroys all data registered to a session.
$session->destroy();

open()close()を複数回呼び出してもエラーは発生しません。内部的には、メソッドは最初にセッションが既に開いているかどうかを確認します。

セッションデータへのアクセス

セッションに保存されているデータにアクセスするには、次の操作を行います。

$session = Yii::$app->session;

// get a session variable. The following usages are equivalent:
$language = $session->get('language');
$language = $session['language'];
$language = isset($_SESSION['language']) ? $_SESSION['language'] : null;

// set a session variable. The following usages are equivalent:
$session->set('language', 'en-US');
$session['language'] = 'en-US';
$_SESSION['language'] = 'en-US';

// remove a session variable. The following usages are equivalent:
$session->remove('language');
unset($session['language']);
unset($_SESSION['language']);

// check if a session variable exists. The following usages are equivalent:
if ($session->has('language')) ...
if (isset($session['language'])) ...
if (isset($_SESSION['language'])) ...

// traverse all session variables. The following usages are equivalent:
foreach ($session as $name => $value) ...
foreach ($_SESSION as $name => $value) ...

情報: `session`コンポーネントを介してセッションデータにアクセスすると、まだ行われていない場合は自動的にセッションが開かれます。これは`$_SESSION`を介してセッションデータにアクセスする場合とは異なり、`session_start()`を明示的に呼び出す必要があります。

配列であるセッションデータを使用する場合、`session`コンポーネントには、配列要素を直接変更できないという制限があります。例えば、

$session = Yii::$app->session;

// the following code will NOT work
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

// the following code works:
$session['captcha'] = [
    'number' => 5,
    'lifetime' => 3600,
];

// the following code also works:
echo $session['captcha']['lifetime'];

この問題を解決するには、次の回避策のいずれかを使用できます。

$session = Yii::$app->session;

// directly use $_SESSION (make sure Yii::$app->session->open() has been called)
$_SESSION['captcha']['number'] = 5;
$_SESSION['captcha']['lifetime'] = 3600;

// get the whole array first, modify it and then save it back
$captcha = $session['captcha'];
$captcha['number'] = 5;
$captcha['lifetime'] = 3600;
$session['captcha'] = $captcha;

// use ArrayObject instead of array
$session['captcha'] = new \ArrayObject;
...
$session['captcha']['number'] = 5;
$session['captcha']['lifetime'] = 3600;

// store array data by keys with a common prefix
$session['captcha.number'] = 5;
$session['captcha.lifetime'] = 3600;

パフォーマンスとコードの可読性を向上させるために、最後の回避策をお勧めします。つまり、配列を単一のセッション変数として保存する代わりに、他の配列要素と共通のキープレフィックスを共有するセッション変数として各配列要素を保存します。

カスタムセッションストレージ

デフォルトの yii\web\Session クラスは、セッションデータをサーバー上のファイルとして保存します。Yii は、さまざまなセッションストレージを実装する以下のセッションクラスも提供しています。

  • yii\web\DbSession:セッションデータをデータベーステーブルに保存します。
  • yii\web\CacheSession:設定済みの キャッシュコンポーネント を使用して、セッションデータをキャッシュに保存します。
  • yii\redis\Session:ストレージ媒体として Redis を使用してセッションデータを保存します。
  • yii\mongodb\SessionMongoDB にセッションデータを保存します。

これらのセッションクラスはすべて、同じAPIメソッドセットをサポートしています。その結果、セッションを使用するアプリケーションコードを変更する必要なく、別のセッションストレージクラスに切り替えることができます。

注記:カスタムセッションストレージを使用しながら$_SESSION経由でセッションデータにアクセスする場合、yii\web\Session::open()によってセッションが既に開始されていることを確認する必要があります。これは、カスタムセッションストレージハンドラーがこのメソッド内で登録されるためです。

注記:カスタムセッションストレージを使用する場合は、セッションガベージコレクターを明示的に構成する必要がある場合があります。PHPの一部のインストール(例:Debian)では、ガベージコレクターの確率が0に設定されており、セッションファイルはcronジョブでオフラインでクリーンアップされます。このプロセスはカスタムストレージには適用されないため、ゼロ以外の値を使用するようにyii\web\Session::$GCProbabilityを構成する必要があります。

これらのコンポーネントクラスの構成方法と使用方法については、APIドキュメントを参照してください。以下は、アプリケーション構成でyii\web\DbSessionを構成して、データベーステーブルをセッションストレージとして使用する例を示しています。

return [
    'components' => [
        'session' => [
            'class' => 'yii\web\DbSession',
            // 'db' => 'mydb',  // the application component ID of the DB connection. Defaults to 'db'.
            // 'sessionTable' => 'my_session', // session table name. Defaults to 'session'.
        ],
    ],
];

セッションデータを保存するための次のデータベーステーブルを作成する必要があります。

CREATE TABLE session
(
    id CHAR(40) NOT NULL PRIMARY KEY,
    expire INTEGER,
    data BLOB
)

ここで「BLOB」は、使用するDBMSのBLOB型を表します。いくつかの一般的なDBMSで使用できるBLOB型を以下に示します。

  • MySQL:LONGBLOB
  • PostgreSQL:BYTEA
  • MSSQL:BLOB

注記:session.hash_functionのphp.ini設定に従って、idカラムの長さを調整する必要がある場合があります。たとえば、session.hash_function=sha256の場合、40ではなく64の長さを使用する必要があります。

または、次のマイグレーションで実現できます。

<?php

use yii\db\Migration;

class m170529_050554_create_table_session extends Migration
{
    public function up()
    {
        $this->createTable('{{%session}}', [
            'id' => $this->char(64)->notNull(),
            'expire' => $this->integer(),
            'data' => $this->binary()
        ]);
        $this->addPrimaryKey('pk-id', '{{%session}}', 'id');
    }

    public function down()
    {
        $this->dropTable('{{%session}}');
    }
}

フラッシュデータ

フラッシュデータは、あるリクエストで設定されると、次のリクエストでのみ使用可能になり、その後自動的に削除される特殊な種類のセッションデータです。フラッシュデータは、ユーザーがフォームを正常に送信した後に表示される確認メッセージなど、エンドユーザーに一度だけ表示する必要があるメッセージを実装するために最も一般的に使用されます。

sessionアプリケーションコンポーネントを使用して、フラッシュデータを設定およびアクセスできます。例えば、

$session = Yii::$app->session;

// Request #1
// set a flash message named as "postDeleted"
$session->setFlash('postDeleted', 'You have successfully deleted your post.');

// Request #2
// display the flash message named "postDeleted"
echo $session->getFlash('postDeleted');

// Request #3
// $result will be false since the flash message was automatically deleted
$result = $session->hasFlash('postDeleted');

通常のセッションデータと同様に、任意のデータをフラッシュデータとして保存できます。

yii\web\Session::setFlash()を呼び出すと、同じ名前の既存のフラッシュデータは上書きされます。同じ名前の既存のメッセージに新しいフラッシュデータを追加するには、代わりにyii\web\Session::addFlash()を呼び出します。例えば

$session = Yii::$app->session;

// Request #1
// add a few flash messages under the name of "alerts"
$session->addFlash('alerts', 'You have successfully deleted your post.');
$session->addFlash('alerts', 'You have successfully added a new friend.');
$session->addFlash('alerts', 'You are promoted.');

// Request #2
// $alerts is an array of the flash messages under the name of "alerts"
$alerts = $session->getFlash('alerts');

注記:同じ名前のフラッシュデータに対してyii\web\Session::setFlash()yii\web\Session::addFlash()を一緒に使用しないでください。後者のメソッドは、同じ名前の新しいフラッシュデータを追加できるように、フラッシュデータを自動的に配列に変換するためです。その結果、yii\web\Session::getFlash()を呼び出すと、これらの2つのメソッドの呼び出し順序によっては、配列が取得されたり、文字列が取得されたりする可能性があります。

ヒント:フラッシュメッセージを表示するには、次のようにyii\bootstrap\Alertウィジェットを使用できます。

echo Alert::widget([
   'options' => ['class' => 'alert-info'],
   'body' => Yii::$app->session->getFlash('postDeleted'),
]);

クッキー

Yiiは、各クッキーをyii\web\Cookieのオブジェクトとして表します。yii\web\Requestyii\web\Responseの両方が、cookiesという名前のプロパティを介してクッキーのコレクションを保持します。前者にあるクッキーコレクションはリクエストで送信されたクッキーを表し、後者にあるクッキーコレクションはユーザーに送信されるクッキーを表します。

リクエストとレスポンスを直接扱うアプリケーションの部分はコントローラーです。したがって、クッキーはコントローラーで読み書きする必要があります。

クッキーの読み取り

次のコードを使用して、現在のリクエスト内のクッキーを取得できます。

// get the cookie collection (yii\web\CookieCollection) from the "request" component
$cookies = Yii::$app->request->cookies;

// get the "language" cookie value. If the cookie does not exist, return "en" as the default value.
$language = $cookies->getValue('language', 'en');

// an alternative way of getting the "language" cookie value
if (($cookie = $cookies->get('language')) !== null) {
    $language = $cookie->value;
}

// you may also use $cookies like an array
if (isset($cookies['language'])) {
    $language = $cookies['language']->value;
}

// check if there is a "language" cookie
if ($cookies->has('language')) ...
if (isset($cookies['language'])) ...

クッキーの送信

次のコードを使用して、エンドユーザーにクッキーを送信できます。

// get the cookie collection (yii\web\CookieCollection) from the "response" component
$cookies = Yii::$app->response->cookies;

// add a new cookie to the response to be sent
$cookies->add(new \yii\web\Cookie([
    'name' => 'language',
    'value' => 'zh-CN',
]));

// remove a cookie
$cookies->remove('language');
// equivalent to the following
unset($cookies['language']);

上記の例で示されているnamevalueプロパティに加えて、yii\web\Cookieクラスは、domainexpireなど、利用可能なすべてのクッキー情報を完全に表す他のプロパティも定義しています。必要に応じてこれらのプロパティを構成してクッキーを準備し、レスポンスのクッキーコレクションに追加できます。

前述の2つのセクションで示されているように、requestコンポーネントとresponseコンポーネントを介してクッキーを読み書きするときは、クライアント側でのクッキーの変更を防ぐクッキー検証の追加のセキュリティが提供されます。これは、各クッキーにハッシュ文字列を使用して署名することで実現され、アプリケーションはクライアント側でクッキーが変更されたかどうかを判断できます。変更された場合、クッキーはrequestコンポーネントのcookieコレクションを介してアクセスできなくなります。

注記:クッキー検証は、クッキーの値が変更されるのを防ぐだけです。クッキーが検証に失敗した場合でも、$_COOKIEを介してアクセスできます。これは、サードパーティライブラリが独自の方法でクッキーを操作し、クッキー検証を伴わないためです。

クッキー検証はデフォルトで有効になっています。 yii\web\Request::$enableCookieValidationプロパティをfalseに設定して無効にすることができますが、そうすることは強くお勧めしません。

注記:$_COOKIEsetcookie()を介して直接読み書きされるクッキーは、検証されません。

クッキー検証を使用する場合は、前述のハッシュ文字列の生成に使用されるyii\web\Request::$cookieValidationKeyを指定する必要があります。アプリケーション構成でrequestコンポーネントを構成することで実行できます。

return [
    'components' => [
        'request' => [
            'cookieValidationKey' => 'fill in a secret key here',
        ],
    ],
];

情報:cookieValidationKeyは、アプリケーションのセキュリティにとって非常に重要です。信頼できる人だけが知る必要があります。バージョン管理システムに保存しないでください。

セキュリティ設定

yii\web\Cookieyii\web\Sessionの両方が、次のセキュリティフラグをサポートしています。

httpOnly

セキュリティを強化するために、yii\web\Cookie::$httpOnlyのデフォルト値とyii\web\Session::$cookieParamsの 'httponly' パラメーターはtrueに設定されています。これにより、クライアント側のスクリプトが保護されたクッキーにアクセスするリスクを軽減できます(ブラウザーがサポートしている場合)。詳細については、HttpOnly wiki記事を参照してください。

secure

secureフラグの目的は、クッキーがクリアテキストで送信されるのを防ぐことです。ブラウザがsecureフラグをサポートしている場合、リクエストがセキュア(TLS)接続を介して送信された場合にのみ、クッキーが含まれます。詳細については、SecureFlag wiki記事を参照してください。

sameSite

Yii 2.0.21以降、yii\web\Cookie::$sameSite設定がサポートされています。PHPバージョン7.3.0以降が必要です。sameSite設定の目的は、CSRF(クロスサイトリクエストフォージェリ)攻撃を防ぐことです。ブラウザがsameSite設定をサポートしている場合、指定されたポリシー( 'Lax'または 'Strict')に従ってのみクッキーが含まれます。詳細については、SameSite wiki記事を参照してください。セキュリティを強化するために、サポートされていないバージョンのPHPでsameSiteを使用すると、例外がスローされます。異なるPHPバージョンでこの機能を使用するには、最初にバージョンを確認します。例:

[
    'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
]

注記:すべてのブラウザがsameSite設定をまだサポートしているわけではないため、追加のCSRF保護を含めることも強くお勧めします。

Session php.ini設定

PHPマニュアルに記載されているようにphp.iniには重要なセッションセキュリティ設定があります。推奨される設定が適用されていることを確認してください。特に、PHPのインストールではデフォルトで有効になっていないsession.use_strict_modeです。この設定は、yii\web\Session::$useStrictModeでも設定できます。

タイプミスを見つけたか、このページの改善が必要だと考えますか?
githubで編集する !