PHPのセッション

PHPでユーザーデータを管理するセッションについて,公式マニュアルを元に勉強したので内容を記す。
基本概念
セッションはシンプルな方法でユーザーのデータを格納する仕組みだ。
従来では,URLのクエリーを使うか,クッキーを使うしかなかった。そして共に以下の問題を抱えていた。
- 受け渡しの処理が煩雑
- 受け渡し可能データサイズの制限
- セキュリティの問題
セッションはこれらの欠点を解消したユーザーデータの格納方法となっている。セッションによるユーザーデータの格納の基本的な概念は以下となる。
- 個々のユーザーに対して一意なセッションIDを発行する。
- 複数ページにまたがるリクエストに対して,同一のセッションIDをクッキー (セッションクッキー) で受け渡す。
- 受け取ったセッションIDを使い,サーバーに格納したセッションデータを取り出す。
セッションIDをクッキーで受け渡すことで,ネットワーク経由では最小のデータの受け渡しで,実際のデータの対応を確認可能となる。なかなかよくできた仕組みだ。
なお,セッションIDに使用されるクッキー名のデフォルトはPHPSESSID
であり,php.iniのsession.name
指令で設定可能だ。
セッション開始
セッションのPHPで実装例は公式マニュアルに記載がある。整理すると以下の手順となる。
session_start
関数を実行して,新規セッションを開始,または既存セッションを再開。isset($_SESSION['var'])
で使用セッションデータの有無を判定し,変数の初期値を設定。- プログラム終了前に
$_SESSION
にデータを登録。
<?php
// 1. Start session.
session_start();
// 2. Start using session data.
if (!isset($_SESSION['count'])) {
$count = 0;
} else {
$count = $_SESSION['count'];
}
/*
Use session data.
*/
// 3. Register session data.
$_SESSION['count'] = $count;
?>
直接$_SESSION
の配列にアクセスしてもよい。しかし,毎回配列にアクセスするのは冗長なので,処理中は変数に代入して扱うとよいだろう。
なお,セキュリティの脆弱性として,セッションハイジャックというものがあり,session_start()
の直後に,session_regenerate_id(true)
を実行したほうがいいらしい。
しかし,脆弱性対策はいまいちよくわからないので,後ほど動作するアプリケーションを作れるようになって知識付いてから取り掛かることにする。
セッション終了
セッション終了時は,確保したセッションデータを破棄する必要がある。セッションデータの破棄のサンプルコードがsession_destroy関数のマニュアルに記載がある。これを整理すると,PHPでのセッション終了は以下の手順で行う。
$_SESSION = [];
でメモリー内のセッション変数を全削除。または,unset($_SESSION['var']);
で使用変数を個別に削除。setcookie
関数でブラウザー上のセッションクッキーを削除。session_destroy
関数でサーバー上のセッションデータを削除。
<?php
// 1. Delete memory data.
$_SESSION = [];
// 2. Delete cookie.
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', 1,
$params["path"], $params["domain"], $params["secure"], $params["httponly"]
);
}
// 3. Delete server data.
session_destroy();
?>
1のセッション変数の削除は,$_SESSION = array();
によるものをよく見かける。同じ意味で関数呼び出しの発生しない[]
で良いだろう。
なお,セッション変数の削除では以下の記載の通りunset($_SESSION)
で$_SESSION
自体を削除してはいけない。これをするとスーパーグローバル変数でなくなるため,セッション変数の登録できなくなる。
Caution
Do NOT unset the whole $_SESSION with unset($_SESSION) as this will disable the registering of session variables through the $_SESSION superglobal.
その他,似たような役割の関数にsession_unset
関数が存在する。この関数は$_SESSION
変数を使っていない昔のコードのためのものなので,基本的に使う必要はない。
Note:
Only use session_unset() for older deprecated code that does not use $_SESSION.
2ではブラウザー上のクッキーを削除する。クッキーの有無の判定には,ini_get("session.use_cookies")
の他に,isset($_COOKIE[session_name()])
も見かける。
isset関数を使う方法は,昔のPHPのsession_destroy関数のマニュアルに記載があったようだ。しかし,最新ではini_get関数を使う方法となっている。こちらのほうが括弧が少なく,スーパーグローバル変数へのアクセスを回避できるので,こちらを採用する。
セッション関係の設定
セッションに関するphp.iniの設定が存在する。全リストは公式マニュアルの「PHP: Runtime Configuration – Manual」に記載がある。ここでは,特に注意が必要な項目を掲載する。
指令 | デフォルト | 説明 |
---|---|---|
session.gc_maxlifetime | 1440 (24分) | セッションデータの有効期限を秒数で指定する。この時間の超過時に,ガベージコレクションの対象となる。session_destroy関数を呼び出さずに,セッションを終了せずに無関係なページに移動するとセッションが残るので,そのタイムアウトをこの設定で行う。 |
session.use_strict_mode | false | 厳格なセッションIDモードの有無を指定する。有効にすると,未初期化セッションIDを拒絶する。未初期化セッションIDがブラウザーから受信した場合,新しいセッションIDを返信する。全サイトで有効化を推奨。 |
session.cookie_secure | “” | セッションIDをクッキーで受け渡し時のSecure属性を指定。基本的にtrueにする。 |
session.use_strict_mode
とsession.cookie_secure
はセキュリティのため,有効にすべきだろう。
session.gc_maxlifetime
は修正は必要ないが,セッションのタイムアウト設定として重要なので,存在を知っておいたほうがいい。
実装例
PHPのセッションを使ったサンプルを作成した。全文を以下に掲載する。
このサンプルは「改訂版 今すぐ導入!PHP×PostgreSQLで作る最強Webシステム」のp. 76に掲載されている「リスト1.8 セッションの簡単な利用例: session.php」を元にしている。
ソースコードはGIthubで公開しており,動作ページも用意している。
<?php
/// \file session.php
/// \author SENOO, Ken
/// \copyright CC0
/// \date Created: 2019-07-28 Sun
/// \sa https://senooken.jp/post/2019/08/03/
/// \brief PHPでのセッションの実装例。
// Start session.
session_start();
if (isset($_POST['logout'])) {
// Delete memory data.
$_SESSION = [];
// Delete cookie.
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', 1,
$params["path"], $params["domain"], $params["secure"], $params["httponly"]
);
}
// Delete server data.
session_destroy();
}
?>
<!DOCTYPE html>
<html>
<head><title>Session Test</title></head>
<body>
<section>
<h1>Session Test</h1>
<?php
// Start using session data.
if (!isset($_SESSION['count'])) {
$count = 0;
} else {
$count = $_SESSION['count'];
}
// Use session data.
if (isset($_POST['countup'])) {
++$count;
} else if (isset($_POST['logout'])) {
print("Logout.");
exit;
}
print <<< EOT
Count: $count <br />
<form method="post">
<input type="submit" name="countup" value="Count up" />
<input type="submit" name="logout" value="Logout" />
</form>
EOT;
// Register session data.
$_SESSION['count'] = $count;
?>
</section>
<section>
<h1>説明</h1>
<p>PHPでのセッションの実装例を作成した。PHPでのセッションの実装方法は<a href="https://senooken.jp/post/2019/08/03/">ブログ</a>で説明している。</p>
<p>このサンプルは「改訂版今すぐ導入!PHP×PostgreSQLでつくる最強Webシステム」のp. 76に掲載されている「リスト1.8 セッションの簡単な利用例: session.php」を元にしている。</p>
<p>[Count up] を選択すると,数字が[1]ずつ増加する。[Logout] を選択すると,ログアウト画面となり,[Count] を [0] にリセットする。</p>
<p>ソースコードと動作ページは以下となる。</p>
<ul>
<li>ソースコード: <a href="https://github.com/senooken/example/blob/master/PHP/session.php">https://github.com/senooken/example/blob/master/PHP/session.php</a></li>
<li>動作ページ: <a href="https://example.senooken.jp/PHP/session.php">https://example.senooken.jp/PHP/session.php</a</li>
</ul>
</section>
</body>
</html>
実際は,ログアウト画面を別ページにして,ファイルを分離するだろう。
結論
PHPのセッションについて整理した。
セッションはログイン機能を実装するために必須の機能であり,今後頻出するだろう。仕組みがよくわかっていなかったので勉強になった。
脆弱性対策は不十分なので,知識が身についてからサンプルをアップデートしていきたい。