PHP 入門

PHP セッション:セッション管理

PHP セッション(Session)は、複数のページリクエスト間でユーザー固有のデータを保持するためのメカニズムを提供します。1回のリクエストしか処理できない $_GET$_POST とは異なり、$_SESSION を使用することで、ユーザーがサイト内をブラウジングしている間、アプリケーションがそのユーザーを「記憶」することが可能になります。この機能は、ユーザー認証(ログイン)、ショッピングカート、パーソナライズされたレコメンデーションなどを実装するための基礎となります。

1. PHP セッションとは

PHP において、セッションはユーザーごとに一意の識別子を作成します。これは一般的に セッション ID (Session ID) と呼ばれます。この ID は通常、クッキー (Cookie) としてユーザーのブラウザに保存されます。ユーザーがサーバーに後続のリクエストを送信すると、ブラウザはそのセッション ID を送り戻し、PHP が関連するセッションデータを取得できるようにします。これらのデータはクライアントサイドではなく サーバーサイド (Server-side) に保存されるため、セッションは機密情報を扱う際、通常のクッキーよりもはるかに安全です。

セッションの主な特徴は以下の通りです:

  • サーバーサイドストレージ: セッションデータはサーバー上に保存されます。これはクライアントサイドのストレージ(クッキーなど)よりも安全で、機密情報の保持に適しています。
  • 一時的な持続性: セッションデータは、単一ユーザーによる複数のページリクエスト間で持続しますが、通常はユーザーがブラウザを閉じるか、あらかじめ設定された一定の非アクティブ時間が経過した後に破棄されます。
  • 一意識別: 各アクティブなセッションは、一意のセッション ID によって識別されます。

2. セッションを開始する

セッション変数(バリアブル)を使用する前に、必ず session_start() 関数を使用して明示的にセッションを開始する必要があります。この関数は、新しいセッションを初期化するか、クライアントから送られてきたセッション ID に基づいて既存のセッションを復元します。

重要なルール: session_start() は、スクリプトの最優先事項として呼び出す必要があります。ブラウザに何らかの出力(スペース、改行、HTML タグを含む)を送信する前に実行しなければなりません。session_start() の前に出力がある場合、エラーが発生し、セッションが正常に初期化されなくなります。

<?php
// スクリプトの最初に必ず session_start() を呼び出します。
// これによりセッションを初期化、または現在のセッションを復元します。
session_start();

// session_start() の後であれば、他の PHP コードや HTML を記述できます。
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>セッションの例</title>
</head>
<body>
    <h1>ようこそ!</h1>
</body>
</html>

3. セッションデータの保存と読み取り

session_start() を呼び出すと、$_SESSION スーパーグローバル配列が使用可能になります。この連想配列を使用して、現在のユーザーセッションに関連するあらゆるデータを保存および取得できます。$_SESSION に保存できるデータは、ストリング、数値、ブーリアン、アレイ(配列)、オブジェクトなど、あらゆる PHP データタイプに対応しています。

3.1 データの保存

データを保存するには、$_SESSION アレイのキー(Key)に値を代入するだけです。

<?php
session_start();

// セッションにユーザー名を保存
$_SESSION['username'] = 'JohnDoe';

// ショッピングカートのアイテム(アレイ)を保存
$_SESSION['cart'] = ['item_id' => 101, 'quantity' => 2];

echo "セッションデータが保存されました。";
?>

3.2 データの読み取り

データを読み取るには、キー名を指定して $_SESSION アレイにアクセスします。

<?php
session_start();

// アクセスを試みる前に、セッションにユーザー名がセットされているかチェック
if (isset($_SESSION['username'])) {
    $username = $_SESSION['username'];
    // ユーザーデータを出力する際は、XSS対策として htmlspecialchars を使用
    echo "おかえりなさい、" . htmlspecialchars($username) . "さん!<br>";
} else {
    echo "セッションにユーザー名が見つかりません。<br>";
}

// カートのアイテムを取得
if (isset($_SESSION['cart'])) {
    echo "あなたのカートの内容:<br>";
    foreach ($_SESSION['cart'] as $key => $value) {
        echo htmlspecialchars($key) . ": " . htmlspecialchars($value) . "<br>";
    }
} else {
    echo "カートは空です。<br>";
}
?>

4. セッションデータの修正と削除

$_SESSION 内の既存のキーに新しい値を代入することで、セッションデータを修正できます。特定のセッション変数を削除するには unset() 関数を使用します。現在のセッションに関連するすべてのデータを破棄し、セッション ID を無効化するには、session_unset()session_destroy() を組み合わせて使用します。

セッション変数の修正:

<?php
session_start();

$_SESSION['page_views'] = isset($_SESSION['page_views']) ? $_SESSION['page_views'] + 1 : 1;
echo "あなたは " . $_SESSION['page_views'] . " ページ閲覧しました。<br>";

// ユーザー名の修正
$_SESSION['username'] = 'JaneDoe';
echo "ユーザー名が更新されました:" . $_SESSION['username'] . "<br>";
?>

特定のセッション変数の削除:

<?php
session_start();

// カートが以前にセットされていると仮定
$_SESSION['cart'] = ['item_id' => 200, 'quantity' => 1];

echo "unset 前のカート:";
print_r($_SESSION['cart']);
echo "<br>";

unset($_SESSION['cart']); // 'cart' 変数のみを削除

echo "unset 後のカート:";
if (!isset($_SESSION['cart'])) {
    echo "カート変数はセットされていません。";
}
echo "<br>";
?>

すべてのセッションデータを破棄し、セッションを完全に終了する:

ユーザーを完全にログアウトさせる、またはセッションをリセットする場合、すべてのセッション変数を解除してからセッション自体を破棄する必要があります。

  • session_unset(): $_SESSION スーパーグローバル配列からすべての変数を取り除きます。セッション自体やセッションクッキーは破棄されません。
  • session_destroy(): セッションに登録されたすべてのデータを破棄します。これは実質的にサーバー上のセッションファイルを削除することを意味します。$_SESSION 配列自体はリセットされないため、クリーンにログアウトするには通常 session_destroy() の前に session_unset() を呼び出します。

また、クライアントサイドのセッションクッキーを明示的に削除することも推奨されます。

<?php
session_start();

// ユーザーがログイン中で、データが $_SESSION にあると仮定
$_SESSION['username'] = 'LoggedInUser';
$_SESSION['user_id'] = 123;

echo "ログアウト前:<br>";
print_r($_SESSION);
echo "<br>";

// 1. すべてのセッション変数をクリア
session_unset();

// 2. セッションを破棄
session_destroy();

// 3. オプション:セッションクッキーを削除して完全にクリーンアップ
$session_name = session_name(); // デフォルトは PHPSESSID
if (isset($_COOKIE[$session_name])) {
    // クッキーの有効期限を過去に設定して削除
    setcookie($session_name, '', time() - 3600, '/');
}

echo "ログアウト後:<br>";
if (empty($_SESSION)) {
    echo "セッション変数がクリアされました。セッションが破棄されました。";
}
echo "<br>";

// 破棄後にアクセスを試みると null/undefined になります
echo "破棄後のユーザー名アクセス:" . ($_SESSION['username'] ?? '未設定') . "<br>";
?>

5. 実践演習:ユーザーログインシステム

ユーザー認証は $_SESSION のクラシックな活用シーンです。ユーザーが正常にログインした後、その識別子(および一部の権限情報)をセッションに保存します。これにより、ページを遷移するたびにパスワードを再入力する手間が省けます。

1. login.php (ログインフォームの表示)

<?php
session_start(); // 既存のログイン状態をチェックするために開始

// すでにログインしている場合はダッシュボードへリダイレクト
if (isset($_SESSION['user_id'])) {
    header('Location: dashboard.php');
    exit();
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ログイン</title>
</head>
<body>
    <h2>ログインページ</h2>
    <?php
    // エラーメッセージ(認証失敗など)があれば表示
    if (isset($_SESSION['error_message'])) {
        echo '<p style="color: red;">' . htmlspecialchars($_SESSION['error_message']) . '</p>';
        unset($_SESSION['error_message']); // 表示後にメッセージをクリア
    }
    ?>
    <form action="authenticate.php" method="post">
        <label for="username">ユーザー名:</label><br>
        <input type="text" id="username" name="username" required><br><br>
                
        <label for="password">パスワード:</label><br>
        <input type="password" id="password" name="password" required><br><br>
                
        <input type="submit" value="ログイン">
    </form>
</body>
</html>

2. authenticate.php (ログイン資格情報の処理)

<?php
session_start();

// POST メソッドで送信されたかチェック
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';

    // ユーザー認証のシミュレーション(実際にはデータベースを照合します)
    // デモ用に 'admin' と 'password123' を正しい資格情報とします
    if ($username === 'admin' && $password === 'password123') {
        // 認証成功:ユーザーデータをセッションに保存
        $_SESSION['user_id'] = 1;
        $_SESSION['username'] = $username;
        $_SESSION['role'] = 'administrator';

        // セキュアなページ(ダッシュボードなど)へリダイレクト
        header('Location: dashboard.php');
        exit();
    } else {
        // 認証失敗:エラーメッセージをセッションに保存
        $_SESSION['error_message'] = 'ユーザー名またはパスワードが無効です。';
        header('Location: login.php');
        exit();
    }
} else {
    // POST 以外での直接アクセスはログインページへ
    header('Location: login.php');
    exit();
}

3. dashboard.php (保護されたページ)

<?php
session_start();

// ログインしているか(user_id がセットされているか)チェック
if (!isset($_SESSION['user_id'])) {
    // 未ログインの場合はログインページへリダイレクト
    $_SESSION['error_message'] = 'このページを表示するにはログインが必要です。';
    header('Location: login.php');
    exit();
}

$username = $_SESSION['username'] ?? 'ゲスト';
$role = $_SESSION['role'] ?? 'user';
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ダッシュボード</title>
</head>
<body>
    <h2>ダッシュボードへようこそ、<?php echo htmlspecialchars($username); ?>さん!</h2>
    <p>あなたのロール:<?php echo htmlspecialchars($role); ?></p>
    <p><a href="logout.php">ログアウト</a></p>
</body>
</html>

4. logout.php (ログアウト処理)

<?php
session_start();

session_unset();
session_destroy();

$session_name = session_name();
if (isset($_COOKIE[$session_name])) {
    setcookie($session_name, '', time() - 3600, '/');
}

header('Location: login.php');
exit();
?>

6. セッション設定とセキュリティのベストプラクティス

PHP セッションは多くの設定オプション(ディレクティブ)を提供しており、これらは動作とセキュリティに直接影響します。php.ini で設定するか、ini_set()session_set_cookie_params() を使用してダイナミックに構成できます。

6.1 主要なセッション設定ディレクティブ

  • session.save_handler: セッションデータの保存に使用するプロセッサを指定します。デフォルトは files(サーバー上のファイル)ですが、分散システムでは memcacheredis がよく使われます。
  • session.use_cookies: セッション ID をクライアントに保存するためにクッキーを使用するかを決定します。デフォルトは ON。強く推奨されます。
  • session.use_only_cookies: URL にセッション ID を含める(URL 書き換え)ことを防ぎます。これは セッション固定攻撃 (Session Fixation) を防ぐための鍵です。モダンな PHP ではデフォルトで ON です。
  • session.cookie_lifetime: クッキーの有効期間(秒)。0 は「ブラウザを閉じるまで」を意味します。
  • session.cookie_secure: true の場合、クッキーは HTTPS 接続経由でのみ送信されます。本番環境では極めて重要です。
  • session.cookie_httponly: true の場合、クッキーは HTTP プロトコル経由でのみアクセスでき、JavaScript からは読み取れません。これにより、XSS によるセッション盗難のリスクを軽減します。

ダイナミック構成の例:

<?php
// session_start() の前にクッキーパラメータを設定
session_set_cookie_params([
    'lifetime' => 3600, // 1時間
    'path' => '/',
    'domain' => '.yourdomain.com',
    'secure' => true,    // HTTPS のみ
    'httponly' => true,  // JavaScript からアクセス不可
    'samesite' => 'Lax'  // CSRF対策をサポート
]);

session_start();

6.2 コアセキュリティ・ベストプラクティス

  • 権限変更時のセッション ID 再生成: ログイン成功時や権限の昇格時には、必ず session_regenerate_id(true) を呼び出してください。これによりセッション固定攻撃を効果的に防げます。
<?php
session_start();
// ログイン成功ロジック...
$_SESSION['user_id'] = $user_id;

// ID を再生成し、古いセッションファイルを削除
session_regenerate_id(true); 
?>
  • 機密情報の保持を最小限に: $_SESSION に平文のパスワードやクレジットカード番号などの高度な機密データを直接保存するのは避けてください。
  • セッションデータの検証: $_GET$_POST と同様に、$_SESSION から取得したデータも常に検証とサニタイズ(クリーンアップ)を行ってください。