セッションとは何か
Webアプリケーションで「ログイン状態を維持する」「カートに商品を入れる」といった機能を実現するためにセッションが使われます。
HTTPはステートレス(状態を持たない)プロトコルのため、リクエストをまたいで情報を保持できません。セッションは、サーバー側にデータを保存し、クライアントにはセッションID(識別子)だけをCookieとして渡すことで、この問題を解決します。
ブラウザ サーバー
| |
|--- リクエスト ----------->| セッションIDを発行
|<-- Cookie: PHPSESSID=abc -| サーバーにデータを保存
| |
|--- リクエスト(PHPSESSID) ->| セッションIDでデータを取得
|<-- レスポンス ------------|
セッションの基本的な使い方
セッションの開始と値の保存
<?php
// セッションを開始する(必ずページの先頭で呼ぶ)
session_start();
// セッションに値を保存する
$_SESSION['user_id'] = 42;
$_SESSION['username'] = 'taro';
$_SESSION['logged_in'] = true;
echo 'セッションに保存しました。';
セッションの値を読み取る
<?php
session_start();
// 値が存在するか確認してから取得する
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true) {
echo 'ようこそ、' . htmlspecialchars($_SESSION['username']) . 'さん!';
} else {
echo 'ログインしてください。';
}
セッションの値を削除する
<?php
session_start();
// 特定のキーを削除する
unset($_SESSION['username']);
// セッション全体をクリアする
$_SESSION = [];
// セッションを完全に破棄する(ログアウト時に使用)
session_destroy();
セッションの設定をカスタマイズする
php.ini や ini_set() でセッションの動作を変更できます。
<?php
// セッション開始前に設定する
ini_set('session.gc_maxlifetime', 1800); // セッションの有効期限(秒)
ini_set('session.cookie_httponly', 1); // JavaScriptからCookieにアクセス不可
ini_set('session.cookie_secure', 1); // HTTPS環境でのみCookieを送信
ini_set('session.cookie_samesite', 'Lax'); // CSRF対策
session_start();
session_set_cookie_params() を使う方法もあります。
<?php
session_set_cookie_params([
'lifetime' => 1800,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
セッションにまつわるセキュリティリスクと対策
セッションハイジャック
攻撃者がセッションIDを盗み、他人のセッションを乗っ取る攻撃です。
対策:ログイン後にセッションIDを再生成する
<?php
session_start();
// ログイン処理(認証成功後)
function login(string $username): void
{
// セッションIDを再生成して古いIDを無効化する
session_regenerate_id(true);
$_SESSION['logged_in'] = true;
$_SESSION['username'] = $username;
}
login('taro');
echo 'ログインしました。';
session_regenerate_id(true) の true は、古いセッションファイルを削除するオプションです。必ず指定しましょう。
セッションフィクセーション攻撃
攻撃者があらかじめ用意したセッションIDをユーザーに使わせる攻撃です。上記と同様、ログイン成功時に必ず session_regenerate_id(true) を呼ぶことで防げます。
セッションの有効期限を自前で管理する
gc_maxlifetime はガベージコレクションのタイミングに依存するため、確実な有効期限管理には自前でタイムスタンプを保存する方法が有効です。
<?php
session_start();
$timeout = 1800; // 30分
if (isset($_SESSION['last_activity'])) {
$elapsed = time() - $_SESSION['last_activity'];
if ($elapsed > $timeout) {
// タイムアウト:セッションを破棄してリダイレクト
session_unset();
session_destroy();
header('Location: /login.php');
exit;
}
}
// アクセスのたびに最終アクティビティ時刻を更新
$_SESSION['last_activity'] = time();
実践:シンプルなログイン機能
セッションを活用した最小構成のログイン機能を見てみましょう。
<?php
// login.php
session_set_cookie_params([
'lifetime' => 0,
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
// 実際はDBと照合する(ここではダミー)
if ($username === 'admin' && password_verify($password, '$2y$...hashed...')) {
session_regenerate_id(true); // IDの再生成を忘れずに
$_SESSION['logged_in'] = true;
$_SESSION['username'] = $username;
$_SESSION['last_activity'] = time();
header('Location: /dashboard.php');
exit;
}
$error = 'ユーザー名またはパスワードが間違っています。';
}
?>
<?php if ($error): ?>
<p><?= htmlspecialchars($error) ?></p>
<?php endif; ?>
<form method="post">
<input type="text" name="username" placeholder="ユーザー名">
<input type="password" name="password" placeholder="パスワード">
<button type="submit">ログイン</button>
</form>
まとめ
PHPのセッション管理の要点を整理します。
| 項目 | ポイント |
|---|---|
| 開始 | session_start() をページ先頭で呼ぶ |
| 保存・取得 | $_SESSION 配列を使う |
| 破棄 | session_destroy() でログアウト |
| ID再生成 | ログイン後に session_regenerate_id(true) |
| Cookie設定 | httponly・secure・samesite を必ず設定 |
| タイムアウト | タイムスタンプで自前管理すると確実 |
セッションは便利な機能ですが、設定を誤るとセキュリティホールになります。今回紹介した対策をセットで実装し、安全なWebアプリケーションを構築しましょう。