[PHPのはじめ方] Part26: ログイン機能の実装(セッション管理)

PHP

Webサイトに必須のログイン機能を、PHPのセッションを使って実装する方法を学びます。

はじめに:なぜログイン機能が必要?

多くのWebサイトやアプリケーションでは、ユーザーごとに異なる情報や機能を提供する必要があります。例えば、SNSでは自分の投稿を見たり、ECサイトでは購入履歴を確認したりしますよね?🧐 こうした「あなた専用」のページを実現するのがログイン機能です。

ログイン機能の裏側では、「今アクセスしているのが誰なのか」をサーバーが覚えておく必要があります。そのための重要な仕組みが「セッション管理」です。今回は、PHPでセッションを使い、基本的なログイン・ログアウト機能を実装する方法をステップバイステップで見ていきましょう!🚀

セッションって何?🤔

セッションとは、一連の関連するリクエスト(Webサイトへのアクセス)の間、ユーザーの情報をサーバー側に一時的に保存しておく仕組みです。

HTTPという通信プロトコルは基本的に「ステートレス」です。つまり、前のリクエストと次のリクエストの間で情報を記憶していません。これでは、ページを移動するたびに「あなたは誰?」と聞かれてしまいます😅

そこでセッションを使います。ユーザーが初めてアクセスしたときに、サーバーはユニークなID(セッションID)を発行し、それをブラウザにクッキーとして保存します。次回以降のアクセスでは、ブラウザがそのセッションIDをサーバーに送ることで、サーバーは「ああ、さっきのユーザーだな」と認識し、関連する情報をセッションストレージから取り出すことができるのです。

ポイント: セッションデータはサーバー側に保存され、ブラウザにはセッションIDのみが保存されます。これにより、重要な情報をブラウザ側に直接保存するよりも安全になります🛡️

PHPでセッションを使ってみよう!

PHPでセッションを利用するには、まず session_start() 関数を呼び出す必要があります。これは、既存のセッションを再開するか、新しいセッションを開始する関数です。

⚠️ 超重要: session_start() は、HTMLなどのコンテンツが出力されるよりも前に呼び出す必要があります。通常は、PHPスクリプトの一番最初に記述します。

<?php
// 必ずスクリプトの最初に記述する!
session_start();

// これ以降にHTMLや他のPHPコードを書く
?>
<!DOCTYPE html>
<html>
<head>
  <title>セッションテスト</title>
</head>
<body>
  <p>セッションを開始しました!</p>
</body>
</html>

セッションにデータを保存するには、PHPのスーパーグローバル変数 $_SESSION を使います。これは連想配列のように扱うことができます。

<?php
session_start();

// $_SESSION 配列にキーと値を指定してデータを保存
$_SESSION['username'] = 'ichiro_tech';
$_SESSION['user_id'] = 123;
$_SESSION['last_login'] = time(); // 現在のタイムスタンプを保存

echo 'ユーザー名: ' . $_SESSION['username'] . ' をセッションに保存しました。';
?>

ログイン処理が成功した場合に、このようにユーザーIDやユーザー名などを保存しておくのが一般的です。

保存したデータを取得するのも簡単です。$_SESSION 配列のキーを指定してアクセスします。

<?php
session_start();

// セッションに 'username' キーが存在するか確認
if (isset($_SESSION['username'])) {
  $username = $_SESSION['username'];
  echo 'ようこそ、' . htmlspecialchars($username, ENT_QUOTES, 'UTF-8') . 'さん!';
} else {
  echo 'あなたはまだログインしていません。';
}
?>

isset() を使って、データが存在するかどうかを確認してから利用するのが安全です。また、ユーザーが入力した可能性のあるデータを表示する際は、htmlspecialchars() を使ってクロスサイトスクリプティング(XSS)対策を行うことが重要です。

ログアウト機能を実装するには、セッションに保存したデータを削除し、セッション自体を破棄する必要があります。

  • session_unset(): $_SESSION 配列の中身をすべて空にします。
  • session_destroy(): サーバー上のセッションデータ(ファイルなど)を削除します。

さらに、ブラウザに保存されているセッションIDのクッキーも削除することが推奨されます。

<?php
session_start();

// 1. セッション変数をすべて解除する
$_SESSION = array();

// 2. セッションクッキーを削除する
// クッキーは過去の有効期限を設定することで削除される
if (ini_get("session.use_cookies")) {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
}

// 3. セッションを破壊する
session_destroy();

echo 'ログアウトしました。';

// ログアウト後にログインページなどにリダイレクトすることが多い
// header('Location: login.php');
// exit;
?>

簡単なログイン機能の実装例 🛠️

これまでの知識を使って、簡単なログインフォームとログイン処理、そしてログイン状態によって表示が変わるページを作成してみましょう。

※ 本番環境では、パスワードは必ずハッシュ化してデータベースに保存し、入力されたパスワードと比較する際には password_verify() 関数を使用してください。ここでは簡単のため、固定のユーザー名とパスワードで比較します。

<?php
session_start();

// もし既にログインしていたら、メインページにリダイレクト
if (isset($_SESSION['user_id'])) {
    header('Location: main.php');
    exit;
}

$error_message = '';

// POSTリクエストがあった場合(フォームが送信された場合)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';

    // 簡単な認証(本来はデータベース等で確認)
    if ($username === 'testuser' && $password === 'password123') {
        // 認証成功
        $_SESSION['user_id'] = 1; // ユーザーIDなどを保存
        $_SESSION['username'] = $username;

        // セッションIDを再生成(セッション固定攻撃対策)
        session_regenerate_id(true);

        header('Location: main.php'); // メインページへリダイレクト
        exit;
    } else {
        // 認証失敗
        $error_message = 'ユーザー名またはパスワードが間違っています。';
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ログイン</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
</head>
<body>
    <section class="section">
        <div class="container">
            <h1 class="title">ログイン</h1>

            <?php if ($error_message): ?>
                <div class="notification is-danger">
                    <?php echo htmlspecialchars($error_message, ENT_QUOTES, 'UTF-8'); ?>
                </div>
            <?php endif; ?>

            <form action="login.php" method="post">
                <div class="field">
                    <label class="label" for="username">ユーザー名</label>
                    <div class="control">
                        <input class="input" type="text" id="username" name="username" required>
                    </div>
                </div>

                <div class="field">
                    <label class="label" for="password">パスワード</label>
                    <div class="control">
                        <input class="input" type="password" id="password" name="password" required>
                    </div>
                </div>

                <div class="field">
                    <div class="control">
                        <button class="button is-link" type="submit">ログイン</button>
                    </div>
                </div>
            </form>
            <p class="mt-4">テスト用: ユーザー名 "testuser", パスワード "password123"</p>
        </div>
    </section>
</body>
</html>
<?php
session_start();

// ログインしていない場合はログインページにリダイレクト
if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}

$username = $_SESSION['username'] ?? 'ゲスト'; // 念のためデフォルト値
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>メインページ</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
</head>
<body>
    <section class="section">
        <div class="container">
            <h1 class="title">ようこそ、<?php echo htmlspecialchars($username, ENT_QUOTES, 'UTF-8'); ?>さん!</h1>
            <p class="subtitle">ここはログインしたユーザーだけが見れるページです。</p>

            <p>あなたのユーザーIDは: <?php echo htmlspecialchars($_SESSION['user_id'], ENT_QUOTES, 'UTF-8'); ?> です。</p>

            <!-- ログアウト処理へのリンク -->
            <form action="logout.php" method="post" style="margin-top: 20px;">
                <button class="button is-danger" type="submit">ログアウト</button>
            </form>
        </div>
    </section>
</body>
</html>
<?php
session_start();

// セッション変数をすべて解除
$_SESSION = array();

// セッションクッキーを削除
if (ini_get("session.use_cookies")) {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
}

// セッションを破壊
session_destroy();

// ログインページへリダイレクト
header('Location: login.php');
exit;
?>

これらのファイルを同じディレクトリに保存し、Webサーバー経由で `login.php` にアクセスしてみてください。ログイン、メインページの表示、ログアウトという一連の流れを確認できるはずです。

セキュリティに関する注意点 🛡️

セッション管理は便利ですが、セキュリティに注意しないと脆弱性の原因となります。

  • セッション固定攻撃 (Session Fixation) 対策: ユーザーがログインに成功したら、session_regenerate_id(true) を呼び出してセッションIDを新しく発行し直しましょう。これにより、ログイン前に推測可能なセッションIDが使われるのを防ぎます。上のログイン処理例にも含まれています。
  • セッションハイジャック対策: HTTPS通信を強制し、セッションクッキーに `Secure` 属性や `HttpOnly` 属性を適切に設定することが重要です。PHPの設定ファイル (`php.ini`) で `session.cookie_secure` や `session.cookie_httponly` を `true` に設定することを検討してください。
  • クロスサイトスクリプティング (XSS) 対策: セッションに保存したデータ(特にユーザー名など)をHTMLに出力する際は、必ず htmlspecialchars() を使用してエスケープ処理を行ってください。
  • パスワードの安全な保存: パスワードは絶対に平文で保存せず、password_hash() を使ってハッシュ化し、password_verify() で検証してください。
  • 入力値の検証: フォームから送信されるユーザー名やパスワードは、必ずバリデーション(形式チェック)とサニタイズ(無害化)を行ってください。
セキュリティはWeb開発において非常に重要です。常に最新の情報を学び、安全なコードを書くことを心がけましょう!💪

まとめ

今回は、PHPのセッション機能を使って基本的なログイン・ログアウト機能を実装する方法を学びました。

  1. session_start() でセッションを開始する(スクリプトの最初に!)。
  2. $_SESSION スーパーグローバル変数にユーザー情報などを保存する。
  3. $_SESSION からデータを読み取り、ログイン状態を確認する。
  4. ログアウト時には session_unset(), session_destroy() を使い、セッションクッキーも削除する。
  5. セキュリティ対策(session_regenerate_id(), htmlspecialchars() など)を忘れない。

セッション管理は、ユーザーごとにパーソナライズされた体験を提供するための基本です。この知識を活かして、よりインタラクティブなWebアプリケーション開発に挑戦してみてください!✨

コメント

タイトルとURLをコピーしました