2003年度後期 IT教育基礎論演習B
各種webアプリケーションを構築するにあたり必要となる HTTPによる継続的なセッションを実現するたのセッション管理手法を学習する。
一般に情報通信におけるセッションとは、 相互に通信を行うノード間での通信開始から終了までの一連のやりとりを指す。 HTTPにおけるセッション管理とは、 関連する複数のセッションをまとめて管理することをいう。
HTTPは、基本的に、 webブラウザから送信される1回のリクエストに対し、 webサーバから1つのwebページが送信する形で1つのセッションが完了する。 従って、あるwebブラウザから同一のwebサイト上の webページにアクセスしている場合でも、 各webページへのアクセスはそれぞれ独立したセッションとなり、 1つのセッションが継続するわけではない。 このため、HTTPは、セッションレス通信であるとも言われる。
このようなセッションレスのHTTPによる通信では、 関連するwebページ群の閲覧においても 独立したセッションにより通信が行われるため、 webサーバでは、それぞれのセッションが同じ利用者からの 継続的なアクセスなのかそれとも非継続的なアクセスなのか、 さらには同じ利用者からのアクセスなのか異なる利用者からのアクセスなのかを 確認することができない (各webブラウザを起動しているクライアントコンピュータには、 それぞれIPアドレスが割り当てられているが、 同じコンピュータ上で異なる利用者がwebブラウザを利用する場合があるだけでなく、 NATやIPマスカレード等により複数のプライベートアドレスを 共通のグローバルアドレスにより接続する場合もあり、 IPアドレスにより同一利用者であるかどうかを識別することはできず、 また、継続的なアクセスか否かも判別できない)。
そのため、HTTPは、その当初の目的であるハイパーテキストのモデルに基づき、 個々に独立したwebページをページ間のリンクを辿って 閲覧していく場合には適しているが、 チャットシステムや電子ショッピングモール、eラーニングシステムなど、 ある利用者による利用開始から終了までの 継続的なアクセスが必要となるアプリケーションには、 そのままでは利用することが難しい。 セッションレス通信であるHTTPを利用し、 継続的なアクセスの必要なwebアプリケーションを構築するには、 関連する複数の独立なセッションを仮想的に1つのセッションとして扱う必要がある。 そのためには、個々のセッションの利用者を特定するとともに、 同一利用者による継続的なアクセスによるセッションであるか否かを判別し、 管理する必要がある。
セッション管理を行う方法の1つとして、セッションIDを利用する方法がる。 これは、仮想的に継続するセッションの開始時に、 そのセッション固有のセッションIDを付与し、 ブラウザからのアクセスの際にそのセッションIDをサーバに送信することにより、 セッションが継続していることを判別するものである。 (バージョン4以上の)PHPでは、 セッションIDによるセッション管理機能が予め組み込まれており、 この機能を利用することにより、 容易にセッション管理を行うことができる。
仮想的なセッションを開始するために、 PHPにおいて新規にセッションIDを付与するには、 session_start()関数を利用する。 session_start()関数は引数をとらず、 この関数が実行されると、既にそのセッションに対して セッションIDが付与されているか否かを判別し、 もし、まだセッションIDが付与されていない場合には、 新規にセッションIDを生成する。 また、付与されたセッションIDを確認するには、 session_id()関数を利用する。
例えば、新規にセッションIDを生成し、仮想的なを開始し、 またそのセッションIDを確認するためには、 以下のようなプログラムを記述することができる。
<?php session_start(); echo("Session ID: ".session_id()."<br>"); ?> |
この実行結果は、以下のようになる (具体的なセッションIDの値は実行結果の例とは異なる)。
Session ID: 8f3a5cad6f2732ef7637315ee9216b6d |
ただし、後述するセッションの継続における Cookieを利用したセッションIDの保存のため、 session_start()関数は、 PHPスクリプトを記述したファイルの先頭で、 まだwebページ文書本体を出力する前に実行する必要があることに注意する。
仮想的なセッションを継続するためには、 ブラウザが新規webページにアクセスする際に webサーバにセッションIDを送信し、 そのページにセッションIDを引き継ぐ必要がある。 PHPにおいて、セッションIDをwebサーバに送信するには、 Cookieを利用する方法と、GETメソッドを利用する方法がある。
Cookieとは、ブラウザ上の小さな記憶領域であり、 webサイト上で利用したい各クライアントごとの様々なデータを クライアント側に保存し、サーバから参照することができる。 このcookieにセッションIDを保存し、参照することにより、 セッションが継続しているか否かを判別することができる。 またcookieは、各利用者のクライアント環境ごとに保存され、 ブラウザによるwebサイトへのアクセスが終了した後にも保存するよう設定できるため、 セッションIDの管理だけでなく、 前回のアクセス日時や利用者名の確認など、 セッション管理された仮想的なセッションの中だけでなく、 異なるセッションをまたいでデータを管理することができる。
Cookieが利用できる場合に PHPでcookieを利用したセッションIDの管理を行うには、 特にcookieの利用を意識する必要はない。 session_start()関数を実行すると、 先ず、既にセッションが開始され、 セッションIDがcookieに保存されているかどうかの確認が行われる。 このとき、まだセッションIDが保存されていなければ、 新規セッションIDを生成すると同時に、 そのセッションIDがcookieに登録される。 また、既にセッションが開始されていて、 cookieにセッションIDが登録されていれば、 そのセッションIDを取得する。
すなわち、セッション管理を行いたい場合、 継続的にアクセスされることとなるwebページ中で 単純にsession_start()関数を実行すればよいこととなる。 例えば、新規セッションIDを生成しセッションを開始するwebページと、 そのセッションIDを継承するwebページは、 それぞれ以下のように記述することができる。
<?php session_start(); ?> <html> <body> <h1>セッション開始</h1> <?php echo("Session ID: ".session_id()."<br>"); ?> <a href="inherit-session-with-cookie.php">次のページへのリンク</a> </body> </html> |
<?php session_start(); ?> <html> <body> <h1>Cookieを利用したセッションの継続</h1> <?php echo("Session ID: ".session_id()."<br>"); ?> </body> </html> |
この実行結果として、先ずセッション開始ページを閲覧すると、 以下のようになる (具体的なセッションIDの値は実行結果の例とは異なる)。
次に、リンクを辿って次のページを閲覧すると、以下のようになる。
Cookieを利用したセッションの継続 Session ID: 2d8f1df95f082b5b1b6d4737efe578b2 |
この2つの実行結果を見ると、 両ページで表示されているセッションIDが同じ値となっており、 最初のセッション開始ページで生成されたセッションIDが 次のページに継承されていることがわかる。
ただし、一度セッションIDが生成されcookieに保存されると、 ブラウザを終了するまでその値が有効であり、 別の無関係なwebページを閲覧した後に、 再度、該当ページにアクセスしたり、 またブラウザの別のウィンドウから該当ページにアクセスしたりした場合でも セッションIDは共通となり、 セッションが継続していることになる。 また、cookieに保存されている値の参照やcookieへの値の保存は HTTPにおけるヘッダ情報により交換されるため、 webページ本体を送信する前に session_start()関数を実行するよう、 プログラムを記述する必要がある。
使用するブラウザの種類によってはcookieを利用できない場合がある。 また個人情報保護などセキュリティ上の理由により、 ブラウザの設定によってcookieの利用を制限している場合もある。 例えば、インターネットエクスプローラの場合、 ツールメニューの中の インターネット オプション(O)...を選択して 表示される設定項目において、 図1に示すプライバシーを「高」に設定することで、 cookieを利用できないように設定することができる。
![]() |
このようにcookieが利用ができない場合にセッションIDを継承するには、 GETメソッドを利用するとよい。 具体的には、リンク先の指定において、 URLに加えて「?」で区切って「識別子=セッションID」を記述することで リンク中にセッションIDを埋め込み、これをGETメソッドにより取得する。
PHPでは、GETメソッドによるセッションIDの受け渡しのために、 特別な定数であるSIDを利用することができ、 これによりセッションIDを埋め込んだリンクを容易に作成することができる。 セッション開始時にsession_start()関数が実行されると、 先ずcookieへのセッションIDの保存を試み、 もしcookieが利用できない場合にはSIDの値として PHPSESSID=<セッションID>を設定する (注: 特に指定がない場合、標準でPHPSESSIDが識別子として設定されるが、 変更することも可能)。 例えば、ブラウザのcookieを無効にし、以下のプログラムを実行すると、 SIDの値を確認することができる (注: 定数SIDの値を参照する場合、「"」などでクオートしてはならない)。
<?php session_start(); echo("SID: ".SID); ?> |
この実行結果は、例えば以下のようになる (ブラウザのcookieの機能を無効にしないと正しく表示されない)。
SID: PHPSESSID=ea5f97ec95b0d5c86363d01e2e9b6a73 |
このSIDを利用し、 PHPプログラムの中でセッションIDを埋め込んだリンクを出力するには、 以下のように記述する。
<?php session_start(); ?> <html> <body> <h1>Cookieを利用しないセッションの開始</h1> <?php echo("Session ID: ".session_id()."<br>"); echo("<a href=\"inherit-session-without-cookie.php?".strip_tags(SID)."\">次のページへのリンク</a>"); ?> </body> </html> |
もしくは、PHPプログラムの中でリンクを出力するのではなく、 HTMLのリンクタグの中でセッションIDを埋め込みたい場合には、 以下のように記述することができる。
<?php session_start(); ?> <html> <body> <h1>Cookieを利用しないセッションの開始</h1> <?php echo("Session ID: ".session_id()."<br>"); ?> <a href="inherit-session-without-cookie.php?<?php echo(strip_tags(SID))?>">次のページへのリンク</a> </body> </html> |
いずれの場合も、実行結果は以下のようになり、 リンク部分にセッションIDが埋め込まれているのが確認できる (cookieが有効になっている場合には埋め込まれない)。
リンク中に埋め込まれたセッションIDはGETメソッドによりリンク先に渡されるため、 リンク先のwebページでは通常のGETメソッドを利用して セッションIDを参照することもできるが、 PHPでは、session_start()関数を実行することにより 自動的にセッションIDとして読み込まれ、利用できるようになる。 従って、セッションIDを受け取りセッションを継続するwebページは、 cookieの利用の可否にかかわらず以下のように記述することができる。
<?php session_start(); ?> <html> <body> <h1>Cookieを利用しないセッションの継続</h1> <?php echo("Session ID: ".session_id()."<br>"); ?> </body> </html> |
セッションを開始したページからリンクを辿り、このページにアクセスすると、 以下のようにセッションIDが継承されていることがわかる。
Cookieを利用しないセッションの継続 Session ID: 2d8f1df95f082b5b1b6d4737efe578b2 |
以上のように、cookieが有効になっていない場合でも、 リンク中にセッションIDを埋め込む形でGETメソッドを利用することで、 異なるwebページ間でセッションIDを継承することができる。
なお、cookieが有効になっている場合には 定数SIDの値はヌル(文字列がない状態)になる。 このため、cookieを利用せずリンク中にセッションIDを埋め込む形で PHPのプログラムを記述しても、cookieが有効であれば、 結果的にGETメソッドで渡される値がない状態と同じになり、 そのままプログラムを実行することができる。 すなわち、セッションIDによるセッション管理を行いたい場合には、 cookieの有効/無効にかかわらず リンク中にセッションIDを埋め込む形でプログラムを記述すれば 同じプログラムを利用することができる。
ただし、cookieが利用できる場合には、 一度セッションIDがcookieに保存されれば、 該当するページに直接アクセスしてもセッションの継続が有効であるが、 cookieが利用できない場合には、 必ず、セッションIDが埋め込まれたリンクを辿らないと、 セッションが継続しているとみなされないといったように、 若干、動作が異なる点に注意する必要がある。
セッションIDの送受信によるセッション管理を行なう場合、 回線を暗号化する必要がある。 本演習で示すサンプルプログラムでは回線の暗号化を行っていないが、 本来、セッション管理のためにセッションIDを利用する場合には、 回線の暗号化を行い、 どのようなセッションIDが利用されているかを 第三者により盗聴できないようにする必要がある。
セッションIDによるセッション管理により複数の関連するセッションを 仮想的に1つのセッションとして扱ったとしても、 基本的には、HTTPにおける個々のセッションは独立であり、 その関連性はwebサーバとwebブラウザ間で交換される セッションIDのみで判別するしかない。 もし、回線のモニタリングなどにより 悪意のある第三者がセッションIDを盗聴できてしまうと、 第三者がそのセッションIDを利用して「なりすまし」を行い、 本来アクセス権のないページやデータにアクセスできてしまうという セキュリティ上の問題となる。
このような問題を回避するためには、 セッション管理のためのセッションIDを盗聴されないよう、 SSLなどにより回線の暗号化を行う必要がある。
セッションIDの受け渡しによるセッション管理だけでは、 HTTPのセッションをまたいで共通な値はセッションIDだけであり、 あまり便利とはいえない。 これに対して、利用者名などを関連するセッションをまたいで 共通に参照できれば便利である。 そのためには、セッションIDと対応付けられた記憶領域をwebサーバ上に用意し、 その記憶領域に値を保存する必要がある。
PHPでは、このようなセッションIDと対応付けられた記憶領域として、 セッション変数$_SESSIONを利用することができる。 $_SESSIONは連想配列となっており、 任意のキーに関連付けた値を保存することができ、 また、その値は各セッションIDごとに固有の記憶領域に記録される。 これにより、セッションIDにより管理されたセッション間で 任意の値を共有することができる。
セッション変数を利用する方法は極めた簡単である。 単純に、session_start()関数を実行して セッション管理されている状態で、 $_SESSION変数への値の代入、参照を行えばよい。 例えば、あるwebページでnameをキーとする値を代入し、 別のwebページでこの値を参照する場合、 それぞ以下のように記述することができる。
<?php session_start(); ?> <html> <body> <h1>セッション変数への値の代入</h1> <?php $_SESSION["name"] = "takoshi"; echo("Registered value for name: ".$_SESSION["name"]."<br>"); echo("<a href=\"refer-session-variable.php?".strip_tags(SID)."\">次のページへのリンク</a>"); ?> </body> </html> |
<?php session_start(); ?> <html> <body> <h1>セッション変数の値の参照</h1> <?php echo("Refered value for name: ".$_SESSION["name"]."<br>"); ?> </body> </html> |
最初のページにアクセスし、またリンクを辿って次のページにアクセスした結果は、 それぞれ以下のようになる。
セッション変数の値の参照 Refered value for name: takoshi |
もし、一度セッション変数に登録した値を破棄したい場合、 通常の配列の要素の破棄と同様にunset()関数を利用する。 例えば、nameをキーとするセッション変数の値を破棄したい場合には、 以下のように記述することができる。
<?php session_start(); ?> <html> <body> <h1>セッション変数の値の破棄</h1> <?php echo("Value for name before unseted: ".$_SESSION["name"]."<br>"); unset($_SESSION["name"]); echo("Value for name after unseted: ".$_SESSION["name"]."<br>"); ?> </body> </html> |
この実行結果は、以下のようになる。
セッション変数の値の破棄 Value for name before unseted: takoshi Value for name after unseted: |
ただし、セッション変数に登録された値を破棄するには、 あくまで登録されたキーに対応する値を破棄しなければならず、 セッション変数そのものをunset()関数により破棄しないよう 注意する必要がある。
トップページのフォームから利用者名を入力すると、 「朝」、「昼」、「夜」の3種類へのページへのリンクを提示し、 またリンク先の各ページでは、利用者名が○○○の場合、 それぞれ「おはようございます、○○○さん」、 「今日は、○○○さん」、「今晩は、○○○さん」と表示し、 もとのページ(3つのページへのリンクを提示しているページ)へ戻る リンクを提示するwebサイトを作成せよ。
ただし、cookieが利用できない場合にも正しく動作するようにプログラムし、 また、トップページ以外は全て暗号化された回線により保護せよ (トップページも含めて保護してもよい)。
なお、この課題に対する回答はレポートに示さなくてよい。
(1)で作成したwebサイトを改良し、 トップページを経由せず利用者名が確定していない状態で、 トップページ以外のページに直接アクセスした場合には、 所定の内容は表示せず、トップページへのリンクを提示するようにせよ。
レポートには、作成したwebサイトのURL(トップページのURL)と、 フォームによる入力のためのHTMLファイル、 入力されたデータを処理し、3つのページへのリンクを提示するPHPプログラム、 リンク先のページを表示するための3つPHPのプログラム、 およびリンクを提示しているページとリンク先ページ (3つのうち、いづれか1つでよい)のスクリーンショットを示せ。