セッションとはWebサーバがクライアント(ブラウザ)を識別してそれぞれのクライアントの状態を管理するための仕組みのことです。Webで使われるHTTPプロトコルは元々スレートレスと呼ばれるものでクライアント側の状態を管理しません。Webサーバはブラウザからのリクエストに応じてページの情報を返すだけのシンプルなものなので、そのページ内でユーザが何をしたかといったことは覚えていません。つまり、ショッピングカートにアイテムを追加してもサーバ側は何も覚えていないのでページを開くごとにフレッシュなページが表示されることになり、カートは常に空という事になってしまいます。今回の記事ではこの問題を解決するための仕組みとしてのセッションについて学習していきます。
セッションの管理の仕組み
ここからセッションの管理の仕組みを見ていきます。
まずはクライアントからページのリクエストを受けたWebサーバはページ情報をブラウザに返すことになりますが、その際に一意となるトークン(文字列)を発行します。それをサーバ内に保管します。トークンと紐づいたこの保存領域をセッション領域と呼びます。その後で同じトークンを含めた状態でブラウザにページデータを返します。Webサーバ側でチケットを発行して、そのコピーをクライアントに渡すイメージですね。
ブラウザ側は受け取ったページデータをブラウザ内で表示すると同時にページデータに含まれているトークンをブラウザのクッキーと呼ばれる領域に保存します。ユーザが欲しい商品、ここではジーンズを見つけてカートに入れるボタンを押下します。その際にカートに入れたいジーンズのデータをWebサーバに送ることになりますが、送信データにクッキーに保存してあったトークンを含めます。
次に、Webサーバはジーンズの情報とトークンの情報を受け取ります。サーバ側にマッチするトークンを見つけたらそのトークンのセッション領域にジーンズの情報を保存して、また同じトークンをページデータに含めてブラウザに返信します。この時、サーバはカートにアイテムがあるという表示状態のデータをブラウザに返します。
ブラウザにはカートアイテムが追加された状態のページが表示され、同時に同じトークンをまたクッキーに保存します。別の商品ページに行っても同じトークンが持ちまわされて、サーバにリクエストが行く度に常にセッション領域が参照されサーバ側からはセッションの状態に応じた応答がブラウザに返されることになります。発行されたチケットのコピーがサーバとブラウザ間で行ったり来たりするイメージになります。
PHPのセッション管理方法
PHPのケースでもう少し詳しき見ていきましょう。セッションは特にログインの状態やショッピングカートによく使用されます。
ApacheとPHPの環境をまず用意する必要があります。セットアップについては「PHPをインストールしてコマンドラインとブラウザでPHPを実行する」の記事を参考にしてください。
最初にPHPの設定を確認しておきます。
index.php
<?php
echo phpinfo();
?>
sessyon欄にある以下の2項目を確認してください。PHPのセッション管理はデフォルトではファイルで行われ、そのセッションファイルがどこのディレクトリに作成されるかが分かります。
session.save_handler files
session.save_path /var/lib/php/session
PHPのセッションの開始・有効化の方法は以下の関数を呼び出した後からセッションファイルへのアクセスが可能になります。初回呼び出し時にセッションファイルが作成され、以降は発行されたセッションID(トークン)にマッチするセッションファイル名を参照することになります。
session_start();
そして、以下の様なsess_を接頭辞としたセッションファイルが作成されます。
sess_6pd5un1ffanvjukcnvn3aa1h1u
サーバからのHTTPレスポンスヘッダーには以下の様にCookieヘッダーにセッションIDが設定されます。
Set-Cookie: PHPSESSID=6pd5un1ffanvjukcnvn3aa1h1u; path=/
サーバからのレスポンスを受け取ったブラウザはレスポンスのSet-Cookieヘッダーを読み取り、PHPSESSIDをキー、6pd5un1ffanvjukcnvn3aa1h1uを値としてブラウザのクッキーにデータを保存します。
そして、ブラウザは次回Webサーバへのアクセス時にはクッキーのセッションデータを読み取り、リクエストヘッダーに以下の様にCookieヘッダーを設定します。
Cookie: PHPSESSID=nbi5b9i0jkb22oni6mk206p87u
セッションデータの管理
セッションデータの設定と取得方法(セッションファイルへの書き込みと読み取り)は以下の通り。
// セッションにデータを保存
$_SESSION['cart'] = 'ジーンズ';
// セッションデータを取得
echo $_SESSION['cart'];
セッションを終了する場合は以下の関数を使用します。
// セッションデータを空にする。
session_unset();
// セッションファイル自体を破棄する。
session_destroy();
セッションは有効時間の設定ができ、期限切れになると自動的にファイルが削除されます。ただし、特にログアウトする際はセッションファイルを明示的に削除する様にしましょう。
セッションのセキュリティー
以下の設定をすることでセッションに関わるセキュリティーを向上させることができます。
【セッション固定攻撃を防ぐ】
session_regenerate_id(true); をログイン後に実行する。
ログイン前後で同じセッションIDを使い続けないように、ログインに成功した場合はセッションIDを作り直します。
session.use_strict_modeをオンにする。
存在しないセッションIDがサーバに送られてきた場合はそのIDでセッションファイルを作成せずにサーバ側で新規に作成したセッションIDを作成してセッションファイルを作成する。
【クロスサイトスクリプティング(XSS)の防止】
session.cookie_httponly をオンにする。
HTTPプロトコル使用時のみクッキーにアクセスでき、Javascriptの様なスクリプト言語からはアクセスできないようにする。
セッションとロードバランサー使用時の注意点
ロードバランサーを使用して複数台のWebサーバを管理する場合、セッションを使用する際には注意が必要です。デフォルトのセッションファイルを使ってセッション管理をする場合、ロードバランサーで振り分けられた先のサーバが前回振り分けられたサーバと異なる場合、それぞれのサーバ上にセッションファイルが作成されてしまい、それぞれが違うデータを持ってしまうことになります。カートに入れたデータが消えた。何回かページを遷移したら出てきた。消したはずのカートアイテムが消えてないという様なことが起こりえます。
ロードバランサーには通常スティッキーセッション(Sticky Session)という機能が付いていて、同じサーバに振り分けられるような仕組みがあります。セッションファイルを使用する場合はこのスティッキーセッションの機能を必ずオンにしておく必要があります。
セッションファイル VS DBセッション
PHPはデフォルトでファイルを使ったセッション管理を行いますが、データベースを使ったセッション管理にも対応しています。それぞれのメリットデメリットを確認してみましょう。
セッションファイルのメリットデメリット
【メリット】
- デフォルト設定なので追加作業がない
- サーバ内のローカルファイルへのアクセスなので高速
【デメリット】
- スケールしにくい
- 複数サーバ間でのセッションの共有の問題がある
- ロードバランサーのスティッキーセッションの設定忘れも発生する
- 複数サーバ間でのセッションの共有の問題がある
- ファイルなのでセッションデータの検索がしにくい
- セッションフォルダの適切な権限管理が必要
DBセッションのメリットデメリット
【メリット】
- スケールしやすい
- 複数サーバ間でのセッション共有がしやすい
- DBの権限や通信の暗号化でより安全にアクセスできる
- DBなのでセッションデータの検索がしやすい
【デメリット】
- 初期設定やコードが必要
- ネットワークを経由するのでローカルファイルへのアクセスに比べると少し遅い
- DBが落ちるとセッションが全滅してしまうリスクがある
データベースを使ったセッション管理
セッション管理にはデータベースが使えますが、ここではメモリストアDBのRedisとRDBMSのMaridDBを比較してみます。それぞれの特徴を把握しておきましょう。
メモリストア(Redis)の特徴
RedisはメモリDBなのでフィジカルにハードドライブにデータを書き込むDBに比べると遥かに高速に処理ができます。なので、高トラフィックで速度最優先の場合はRedisはおすすめです。一方で、データの保存方法がキー・バリュー形式(とその他いくつかありますが)なので、セッションデータの検索には向いていないと言えます。
Redisをセッションストアとして使用する場合は以下の記事を参考にしてください。

RDBMS(MariaDB)の特徴
RDBMSはデータの永続化に向いているのでセッションデータを保存してデータ分析する場合などに適しています。クエリが使えるのでデータの検索がしやすいです。既にMariaDBを使用している場合や、Redisの導入が難しい場合はそのままMariaDBをセッションDBとして使用するのもよいでしょう。一方でメモリDBではないのでRedis程高速な処理はできないので高トラフィックには向いていないと言えます。
MariaDBにもMEMORYストレージエンジンがありメモリDBとして使用することはできますが、データの永続化が完全に不可能で再起動時にデータが失われてしまうのが注意点です。本来の目的は一時テーブルとして使用するとこなので、メモリDBとして高速なパフォーマンスが必要であればRedisの導入を検討した方がよいでしょう。
MariaDBをセッションストアとして使用する場合は以下の記事を参考にしてください。

まとめ
今回の記事ではPHPのセッションの管理方法について学習しました。セッション管理の概念的な理解をしてから実際のPHPの設定を確認しました。PHPはデフォルトではファイルでセッションを管理していましたね。セッションIDの持ちまわしの方法を確認した後はセッションのセキュリティーの設定についても学びました。ロードバランサーを使用する際にはスティッキーセッションをオンにするのを忘れてはいけませんでした。そして、ファイルとデータベースでのセッション管理の違いについても理解を深めました。簡単に使えて高速なセッションファイル、スケールしやすく検索しやすいのはDBセッションでした。最後にはデータベースセッションとして、高速なRedisと分析に向いたMariaDBの2つについても特徴をつかみました。これでシステムの要件に合った適切なセッションの運用方法を選択できるようになりました。