セッションの利用~  365日の紙PHP(9日目)

近代的なPHP開発を行うために

おススメ!記事
Raspberry Pi 用「HAL」で、
カップラーメン・タイマーを作ってみよう!
ラズパイDIYの決定版! ソケットサーバー「HAL」をご紹介します。

さて、昨日はログインフォームからの送信先を自分自身にすることで、フィールドに入力された内容が消えないような改造を施しました。

通常、ログインした先には複数のページが存在します。ログインしないでそれらのページにアクセスできては困りますから、ユーザーがログインしているかどうかを各ページで判定する必要がでてきます。

どのようにしてログイン済みのユーザーであるかを判定したら良いでしょうか?

ユーザーのブラウザにIDとパスワード情報を持たせておいて、ログインフォームの時のようにアクセスする度にそれを送信するのはどうでしょう? それはあまり良い方法ではないでしょう。IDとパスワードは重要な情報なのでそう頻繁に送信すべきではありません。

では、ログインしたらチケットを発行してユーザーのブラウザに持たせ、アクセするたびにそのチケットを送信しさせたらどうでしょう?

これはなかなか良さそうですね。サーバー側は、送信されたチケットが正しいものか調べる事でログイン済みかどうかを判断できます。正しくなければログイン画面にリダイレクトさせます。

今日はこの機能を実装してみましょう。

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

このようなログイン管理をする時、PHPでは通常、セッションという機能を使います。

まず index.php の先頭で、セッション機能を使う宣言をしましょう。

<?php

session_start();

$allow = array(
"taro" => '$2y$10$9xUPdF/l2deV2gCo2BbqAeJ6Znh1eTDfQ4e7Rgy0Jy8lzvmyYIZcu',
"hanako" => '$2y$10$nBaOXLHZgXAGobWD1JZMxOnLUwQ1BAYdxJpyqQSJRfVxRwQ/o6ln2',
"kenji" => '$2y$10$u2GdyeCYUtImZAgkyqlpYeWrIsuLwmXtsxCzCOX7NHhEO/q0AkBE.',
);

このように session_start(); を宣言することで、セッション機能が有効になります。注意点としては session_start() する前に文字列等を出力してはいけません。このため、通常はPHPコードの最初の方で session_start() する事になります。

次は、IDとパスワードの照合が成功したら、login.php にリダイレクトする前にセッション変数を定義します。

    if($login_ok === true){

$_SESSION["logged"] = true;
$_SESSION["user_id"] = $_POST["user_id"];

http_response_code(200);
header("Location: login.php");
exit;
}

$_SESSION["logged"] が配列であるということはもうお分かりいただけると思います。$_SESSIONに、logged というキーで true (真) を、user_id というキーで入力された user_id を設定しています。

最後に、ログイン後の画面である login.php の先頭に次のようなPHPコードを書きます。HTML内の$_POST["user_id"] は $_SESSION["user_id"] にします。

<?php

session_start();

if(!isset($_SESSION["logged"])){
http_response_code(403);
header("Location: index.php");
exit;
}

?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>ようこそ</h1>
<p>ログイン処理が成功しました。ようこそ<?php echo $_SESSION["user_id"] ?>さん。</p>
</body>
</html>

先頭では再び session_start() を記述し、このページでもセッションを使うことを宣言しています。

あとは、$_SESSION["logged"] が定義されていなければ index.php にリダイレクトする処理ですが、if文の !isset() は () 内の値が定義されていなければ、という意味です。 isset() は () 内の値が定義されていればという意味で、! はその反対となっています。

ここまでコードを書いたら実際にログインしてみましょう。正しいIDとパスワードの組み合わせでログイン出来ることを確認して下さい。

altテキスト

確認したら、一旦ブラウザをすべて閉じ、再びブラウザを起動して今度はアドレスバーに直接

http://localhost/login.php

を入力してみましょう。index.php にリダイレクトされたはずです。

altテキスト

どういう仕組みになっているか調べてみましょう。Firefox か Chrome でログインの処理をした後、以下のような操作をして下さい。

Firefoxの場合

右上のバーアイコンから開発ツールボタンをクリックし、開発ツールバーを選んで下さい。

altテキスト

画面下に開発ツールバーが表示されるので、そこに cookie list とタイプしてエンターを押して下さい。

altテキスト

以下の様なリストが表示され、その中にPHPSESSIDという値があるはずです。

altテキスト

Chromeの場合

右上のバーアイコンからその他のツールボタンをクリックし、デベロッパー ツールを選んで下さい。

altテキスト

デベロッパーツールが表示されるので Resources から Cookies の localhost で PHPSESSID を確認して下さい。

altテキスト

PHPSESSID とは

PHPSESSID というのは、PHPでのデフォルトのセッションIDキーです。これは PHP の設定ファイルである php.ini に定義されています。

XAMPP の方はコントロールパネルの Apache の Config から PHP (php.ini) で内容を確認して下さい。MAMP の方は /Applications/MAMP/bin/php/php5.6.10/conf/php.ini にあるはずです(PHPのバージョンが違う場合は読み替えて下さい)。

php.ini で [Session] を検索してみましょう。次のようなコードが確認できると思います。(抜粋)

[Session]
; Handler used to store/retrieve data.
; http://php.net/session.save-handler
session.save_handler=files

(略)

session.save_path="D:\xampp56\tmp"

; Whether to use cookies.
; http://php.net/session.use-cookies
session.use_cookies=1

(略)

; Name of the session (used as cookie name).
; http://php.net/session.name
session.name=PHPSESSID

(略)

上記の session.name が、デフォルトのセッションID用のキーです。session.use_cookiesが1になっているので、セッションIDは cookie でユーザーに渡されます。また、session.save_handler が files になっていますのでセッションはファイルとして保存され、その場所は session.save_path の場所です。こちらの環境では XAMPP を xampp56 という名前でDドライブにインストールしたので上記のようになっています。

さて、では session.save_path に指定されている場所のフォルダを開いて、セッションがどのようなファイルで保存されているか見てみましょう。

altテキスト

保存先にはこのように複数のファイルがありますが、sess_ で始まるファイルが PHP のセッション保存ファイルです。sess_ の後に先ほど確認した PHPSESSID の値が続くファイルをテキストエディタ開いてみましょう。

altテキスト

logged|b:1;user_id|s:4:"taro";

このような値が保存されています。logged と user_id はキーですね。|でキーと値を分離しています。そのペアを ;(セミコロン)で区切っています。

b は boolean(真偽値)の略です。先ほど $_SESSION["logged"] に true を設定しました。このようにプログラムでは false と 0、true と 1 を同等に扱うことがよくあります。

s は string(文字列)の略です。その後の数字は文字列の文字数のようですが、正確には文字列のデータ量でバイト数です。日本語の文字は1文字で数バイトが必要であったりしますので見かけ上の文字数とここの数字は一致しないことが多くあります。

どうでしょう? ブラウザに cookie というもので記号の羅列が渡されていて、$_SESSION に設定した値がその記号の羅列のファイル名で書きだされていることはお分かりいただけたでしょうか?

この記号の羅列が最初にお話したチケットの役割をしています。

PHP で session_start() を宣言すると、その接続についてセッションIDと呼ばれる他のユーザーとは重複しないランダムな文字列を PHP が自動で発行し、それをユーザーのブラウザに返します。そして接続が終了したら発行したセッションIDで、そのセッションに設定された値を保存します。

ユーザーのブラウザにはセッションIDが cookie で渡されているので、そのブラウザで再びサーバーにアクセする時には自動でセッションIDが送信されます。再びアクセスされたページで session_start() が宣言されていると、PHP は cookie で渡されたセッションIDを元に書きだされたファイルを読み込んで、前回保存した値を復元して使えるようにします。

以後、これが繰り返されます。

初めてアクセスしてきたユーザーはセッション変数が設定されていないのでログイン画面に強制的にリダイレクトされますが、1度ログインしたユーザーは cookie にセッションIDが記録されていてそれが送信されるため、サーバーのセッション保存ファイルが削除されない限りログイン後の画面にアクセスできるわけです。

セッションの保存時間

セッションファイルの保存時間はデフォルトで1440秒(session.gc_maxlifetime)で、つまり24分ですが、この時間を超えたら必ずセッションファイルが削除されるわけではありません。まず、ユーザーが誰もアクセスしてこなければセッションファイルはずっと残っています。

そして、誰かがアクセスしてくる度に php.ini で設定されている session.gc_probability / session.gc_divisor の確率で削除されます。例えば XAMPP では session.gc_probability = 1、session.gc_divisor = 1000 のようなので、session.gc_maxlifetimeを超えたセッションファイルは 1/1000 の確率で削除されます。

セッションの使い方は大体おわかりいただけたでしょうか?

纏めると、セッションを使うには、

  1. session_start() する。その前に出力は行わない。
  2. セッションには$_SESSIONで値を保存できる。
  3. セッションIDはデフォルトで cookie に保存される。
  4. サーバーのセッション保存時間はデフォルトで最短24分。

さしあたって、この4つが理解できればセッションは使えます。それほど難しくはありませんね。

ただし...

もう一つ、知っていなければならないとても重要な事があります。それを知らないと、あなたが作るシステムにはとても大きな穴が開いているのと同じ状態になってしまいます。

セッションは便利なのでほとんどのシステムで利用することになると思いますから、絶対にこれは理解していなければなりません。

その重要な事については、明日ご説明します。

もし余裕がある方は、今日のところは php.ini の設定を眺めてみたり、セッション変数をいろいろといじって試してみてください。

お疲れ様でした。

この記事へのコメント

※現在コメントはMarkdown記法が強制です。>>Markdown の書き方


!isset($_SESSION["logged"])の真偽値について

お名前:marimilque

2016-08-27 16:02:49

貴WEBのPHPプログラミング講座(365日の紙PHP)の充実した内容に8日まで順調に消化してきましたが、9日目で、1週間ほど停滞しています。if文の条件分岐というあまりにも基本的なことなので何を質問しようか迷ったのですが、WEBを検索しても有効な情報が得られませんのでアドバイスをお願いいたします。 > isset() は () 内の値が定義されていればという意味で、! はその反対となっています。 正しくIDとパスワードを入力した場合は、logged=1 (true)で!isset($_SESSION["logged"]はfalseとなり、if文以下の実行をスキップするのが期待値なのですが、私の環境では添付図(デバッグ結果)のようにステップインしてしまいます。結果、login.phpが呼ばれず、index.phpへ逆戻りしてしまいます。 何回コードを見てもおかしな点がわからないのでお手数ですが、アドバイスをお願い致します。

お名前:管理者ウェブサイト

2016-08-28 11:00:04

marimilque様 当サイトをご利用いただき、誠にありがとうございます。 添付のキャプチャ画面ですと $\_SESSION に設定されているのは "Logged" で、 isset() で検査している $_SESSION のキー名は "logged"ようのうです。 $\_SESSION のキー名は大文字と小文字を区別しますので、そこが問題ではないかと思います。 $\_SESSION["Logged"]を設定している箇所をご確認いただけますでしょうか? ※余談ですが、PHPでは function名は大文字と小文字を区別しません。これは歴史的な背景から互換性を維持するためにそのようになっています。

この記事に返信

このコメントに返信