セッション(Session)について

目次

セッション(Session)とは何ぞや?

セッションはPHPでは最重要な機能の一つです。特に対話側のアプリケーションを作成する場合には、セッション機能を使わずに気の効いたことをするのはほとんど不可能です。

セッションは2つの意味で使われます。1つ目はページをまたがる一連の操作という意味で、これが本来のセッションの意味です。

もう1つが、一連の操作の間の情報の維持のための仕組みという意味です。PHPでセッションと呼ぶときは、大抵この機能・仕組みを指します。

ウェブは本来ステートレスです。これはページをまたがれば前のページの情報は覚えていないことを意味します。これをステートフルに扱うための仕組みがセッションです。

これを実現する簡単な方法はクッキー(Cookie)を使うことです。クッキーを使えば、接続をまたがって情報を維持出来ます。

ただし、クッキーには問題もあります。一つは大きさに制限があること、そして容易に改ざん出来てしまうことです。

これに対するPHPの答えがセッションです。セッションはごく大雑把にいえばサーバー側クッキーと言えます。クライアントに保存しておくのはキーだけです。サーバー側にはキーに紐づく残りの情報をすべて保存します。

サーバー側クッキーという捉え方でセッションを理解すると、セッションの理解が深くなると私は考えます。

セッションとは、平たく言えば、サーバー側クッキーである。

つまり、セッション変数の変更は即時に行われるということです。これはクッキーの変更が次のリクエストまで確認できないのとは対照的です。

セッション(Session)の使い方

セッションの使い方は表面上は簡単に見えるかも知れません。

しかし、本質的な意味を理解しないまま使用すると、思わぬ不具合やセキュリティー的な問題を内包しかねません。たとえば、セッションハイジャック等の問題です。

セッションがどのように機能するのかという原理をよく理解したうえで実装することが大切です。そのためにはクッキーの仕組みの理解も不可欠です。クッキーがどのようなものか理解していない場合には、まずはクッキーを深く知ることをお薦めします。

セッションはクッキーという仕組みを前提として成り立っています。
まずは、クッキーを理解することがセッションを理解する近道です。

セッション関連の関数

セッション関連の関数は役割的に大きく2つに分けられます。

1つはセッションの動作を規定するためのもの、もう1つはセッションの動作そのものです。

ini_setのみで設定可能なものもあれば、専用の関数が別に用意されているものもあります。

セッション関連の設定オプションはすべてユーザースクリプトレベルで変更できるものばかりです。

セッション関連関数の歴史的な経緯

セッション関連関数は、PHPのバージョンを経るごとに改善・変化してきました。今となっては推薦されない使い方もあります。

しかしながら、PHPのサンプルや実地のソースでは旧来のものが含まれるので、それが当時としてはどういう意味だったのかということを理解しないといけません。そのためには若干歴史的な経緯も知っておく必要があるかもしれません。

もし、新しくPHPを始めるのであればラッキーです。最新の推奨される方法でセッションを利用できるからです。ただし、この場合でもソースを参考にするときには注意が必要です。決してそのまま鵜呑みにはしないほうがよいでしょう。

例えば、session_is_registered()session_register()などの関数はもはや非推奨

unset,isset,empty,destroy

変数の扱いに関してPHPと他言語の違いを知ることも必要です。他言語ではunsetとemptyはほぼ同じ意味をもつかもしれませんが、PHPではまったく意味が異なります。変数の解放という概念自体もプログラム言語によってはないかもしれません。変数については、PHPを使用するうえでもっともよく理解すべき項目です。

セッションの開始

session_start();

毎回自動的にセッションを開始するには、

session.auto_start = 1

ただし、オブジェクトを復元するためにはクラス定義を先に読む必要があるが、このオプションを採択するとクラス定義を先に読めないためにオブジェクトの復元が出来ない副作用がある。(オブジェクト以外のスカラー変数、配列を使用している場合は問題ありません。)

参考)OPENPNEのconfig.phpの記述

		ini_set('session.gc_maxlifetime', 432000); // 5 days
$GLOBALS['OpenPNE']['common']['session_lifetime'] = 0;
$GLOBALS['OpenPNE']['common']['session_idletime'] = 432000; // 5 days
define('OPENPNE_SESSION_CHECK_URL', true);
(init.inc)
ini_set('session.use_cookies', '1');
ini_set('session.use_only_cookies', '1');
ini_set('session.cookie_path', $url['path']); ←OPENPNE_URL+"/"
OpenPNE_Auth::set_session_save_handler();←SESSION_SAVE_DBがTrueに設定されているときのみ有効。

isset($GLOBALS['OpenPNE']['common']['session_lifetime'])
    or $GLOBALS['OpenPNE']['common']['session_lifetime'] = 0;
isset($GLOBALS['OpenPNE']['common']['session_idletime'])
    or $GLOBALS['OpenPNE']['common']['session_idletime'] = 0;

OpenPNE_Auth()で、ini_set('session.use_cookies', 0); としている。

設定関数

session_cache_expire — カレントのキャッシュの有効期限を返す

 ini_set(‘session.cache_expire’,val)と同じ
session_cache_limiter —
カレントのキャッシュリミッタを取得または設定する ini_set(‘session.cache_limiter’,val)と同じ

session_save_path — 現在のセッションデータ保存パスを取得または設定する

 ini_set(‘session.save_path’,val)と同じ
session_name —
カレントのセッション名を取得または設定する ini_set(‘session.name’,val)と同じ

初回のアクセスを判定する

sidを参照しただけではそれが初回のアクセスなのかどうかまでは分かりません。初回かどうかはセッション変数が定義されているかどうかで判定します。isset($_SESSION[‘KEY’])

セッション変数の解放

不要になったセッション変数はunsetで解放します。個別の変数ではなくすべてのセッション変数を解放する場合は、次のように記述します。

→ セッション変数を解放する意味を記述せよ!!

$_SESSION = array();

クッキー以外のセッションIDの渡し方

  • URLのパラメータ(GET)
  • URLの一部
  • hidden属性(POST)

session.use_trans_sid = on

とすることで、相対リンクに自動的にsidを渡すクエリーが追加され、hiddenテキストが埋め込まれるようになります。(絶対パスで書かれたリンクにはsidが付加されません。これはsidが外部に漏れないための仕様です。ただし、例えそれが自サイトであっても絶対パスで記載すると別サイトと見なされてしまいます。相対パスで記述したにせよ最終的には絶対パスに置き換えているはずなので個人的にはこれはURL展開して判定してほしいと強く思いますが。)

session.use_trans_sidをonにしない場合は、自分でsidを渡すように考える必要があります。

セッション関連の設定について

use_cookiesはクッキーが使える状況ではクッキーを使い、使えなければその代替手段を使うという意味。

use_only_cookiesは何が何でもクッキーしか使わない。使えないならセッション機能は使えない。

use_only_cookies=1ならuse_cookies=1でなければ矛盾するが、use_only_cookiesのほうが優先される。

use_only_cookies=1の場合でも、use_trans_sidは有効だが、それをセッションIDとして認識してくれないので実質意味がない。

セッション情報をデータベースに格納する

セッション情報はデフォルトでファイルに保存されます。データはシリアル化された形式で保存され他のプログラム言語から参照することが可能ですが、セッション情報をデータベースに格納することによりセッション情報の共有が容易になります。

ユーザー定義のセッションハンドラを定義するには、open,close,write,read,destory,gcの6つのイベントに対してハンドラ関数を定義する必要があります。SQLiteの場合にはあらかじめ定義済みのハンドラが使用できます。

session.save_handler = sqlite;

session.save_path = “…/xxx.db”

セッションハイジャック防止策

クッキーを使うように制限する

セッションIDの送信にクッキーだけを用いてURL埋め込みさせない。この場合はクッキーを許可していないユーザーはセッション機能が使用できません。

session.use_cookies = 1

session.use_only_cookies = 1

環境変数をチェックする

同じサイト内からのアクセスかどうかをチェックすることでセッション流用をチェックします。(最初のページ以外のページには同一ドメイン内からアクセスされるはずなので)

session.refrer_check = “http://foo.com

リファラーがこの値を含まない場合は新しいセッションIDを発行します。(古いセッションは維持されます。つまり正しいリファラーからのリクエストが再びあるとセッションは復活します。)

ただし、環境変数HTTP_REFEREはセキュリティーソフトにより渡さないようになっていることがあったり、ユーザーが改ざん出来る情報であるため100%信用することはできません。


テキストエディタに記述したURLをクリックしたり、URLにアドレスを直入力した場合、リファラーが空となりこのチェックをパスしてしまいました。リファラーが空ならエラーとすべきだと思うのですが。。。

SSL/TLSページ以外にクッキーを送信しない

SSL/TLSページではクッキーは暗号化されて送信されるので安心ですが、それ以外のページにもクッキーは送信されてしまいます。SSL/TLSページだけに送信するようにするにはクッキーを作成する際にsecureオプションを付けます。

session.cookie_secure = 1

ユーザー環境が変化した場合にセッションを破棄する

サーバー変数の内容をチェックし、変更があった場合はセッションを破棄します。ただし、この方法は本来のユーザーに不備を与える可能性があります。チェックはmd5などのハッシュを使用するのが一般的です。

セッションハイジャックに関するまとめ

これらの方法は一定の効果はあるものの完璧な対策とは言えません。本当にセキュアに処理する必要がある場合にはSSL/TLSによる暗号化は不可欠です。

関連項目

キャッシュ | クッキー | SQLite

セッションの固定化

よかったらシェアしてね!

この記事を書いた人

コメント

コメントする

目次
閉じる