Contents
HTTP CookieとSession IDとは
元々 HTTP はステートレスなプロトコルなので、プロトコルとしてクライアントを識別することができません。
ならばということで当時最も流行していたブラウザの1つであったネットスケープナビゲーターの制作会社、ネットスケープコミュニケーションズ社が1994年にクッキーを提案・実装しました。
サーバはクッキーという Web ブラウザの仕組みを通じてクライアントコンピュータにデータを保存することができます。
このクッキーファイルにクライアントを一意に識別する値を保存することで、サーバはクライアントが識別できるようになります。
クライアントのクッキーとサーバの双方で保存する識別子の正体が「セッションID」です。
クッキーとセッションIDを見てみる
実際にクライアントのクッキーとセッションIDを見てみましょう。
上図のように、クッキーには PHPSESSID という名前でセッションIDが保存されています。クライアントはこのクッキーをサーバへのリクエストに含めることで、サーバは記録しているセッションIDと紐づけてクライアントを識別することができます。
PHPでセッションIDを生成する
PHP でセッションIDを発行する処理を見てみます。
<?php
//page1.php
session_start();
print session_id().'<br>';
print '<a href="page2.php">page 2</1>';
?>
<?php
//page2.php
session_start();
print session_id().'<br>';
?>
session_start() はクライアントから受け取ったクッキー(セッションID)と自身の持っているセッションIDを比較し、一致した場合はセッションを再開し、一致しなかった場合はセッションIDを作成しクライアントに送信します。
この場合、page1.php ではセッションIDがクライアントに送付され、page2.php ではセッションIDを基にセッションを再開しています。
page1.php にアクセスした際の HTTP メッセージを確認してみると、Response で「Set-Cookie: PHPSESSID=1fcl5obuphmab8ldm33m5ss6ls」が返ってきています。page2.php へのアクセスでは Requset で「Cookie: PHPSESSID=1fcl5obuphmab8ldm33m5ss6ls」を送信しています。この時の Response には PHPSESSID は含まれません。
Set-Cookieについて
session_start() を実行すると自動で「Set-Cookie: PHPSESSID=…」というキーと値がクッキーに記録されます。
クッキーには同名の「setcookie()」という関数が存在し、任意のキーと値をクッキーに記録することが出来ます。
<?php
setcookie ('abc','hogehoge',time()+3600);
?>
上記のように session_start() 無しで単独で機能し、クッキーを食べさせることが出来ます。
クライアントは以降「Cookie: abc=hogehoge」をリクエストに含めますが、session_start() による処理がないのでセッションIDに紐づいた処理にその値を利用することが出来ません。
session_start()について
session_start() は setcookie() を行うだけではなく、その他の特別な処理も行います。例えば session_start() が実行されると、サーバ側では名前にセッション名を含んだファイルが作成されます。
php.ini の session.save_path に保存場所が記載されており、例えば Xampp のデフォルトだと「C:\xampp\tmp」に「sess_セッションID」という
名前でファイルが生成生成されます。
$_COOKIEと$_SESSION
$_COOKIE はクライアントから渡されたクッキーが格納されている連想配列です。
<?php
//page1.php
setcookie ('abc','hogehoge');
print '<a href="page2.php">page2</a>';
?>
<?php
//page2.php
print $_COOKIE['abc'];
?>
session_start() していないのに PHPSESSID があるのは、PHPSESSID の有効期限が切れていない為です。
一方 $_SESSION はクライアントから渡されたクッキーの中に格納されているセッションID(PHPSESSID)に紐づけて作成される連想配列です。
以下のようにセッションIDも $_COOKIE の一要素として取り出すことができます。
<?php
//page1.php
session_start();
print '<a href="page2.php">page2</a>';
?>
<?php
//page2.php
print $_COOKIE['PHPSESSID'];
?>
$_SESSION は $_COOKIE と異なり、サーバ側にデータが保存されます。
また、$_SESSION や $_COOKIE はスーパーグローバル変数なのでどこからでもアクセスが可能です。
<?php
//page1.php
session_start();
$_SESSION['animal']='dog';
print session_id().'<br>';
print '<a href="page2.php">page 2</1>';
?>
<?php
//page2.php
session_start();
print session_id().'<br>';
print $_SESSION['animal'];
?>
つまり $_COOKIE と $_SESSION の最大の違いは値をクライアントに保存するのかサーバに保存するのかという点です。
$_COOKIEと$_SESSIONの使い分け
当然センシティブな情報をクッキーに保存するのは NG なので、setcookie して $_COOKIE で取り出すよりも基本的には $_SESSION を利用する機会の方が多いと思います。
session_regenerate_id(true)
ログイン制の Web サイトなどでは、ログイン時にセッションを作成し、以降は HTTP リクエストに正統なセッションIDが含まれていれば、$_SESSION で定義しているフラグなどでログインしている状態とみなすことができます。
// RequestクッキーのPHPSESSIDでセッションがあるか確認し、
// セッションに'user_name'がセットされていれば許可する。
if(isset($_SESSION['user_name'])) {
print 'ようこそ!';
} else {
print 'ダメです。';
exit;
}
よって他人のセッションIDが分かってしまえば、そのセッションIDを HTTP リクエストに含めることで他人のログイン状態を乗っ取ることができてしまいます。
その対策として session_regenerate_id() があります。
session_regenerate_id() を実行するとセッションIDが再生成されます。
セッションIDは新しくなりますが、セッション状態は維持されます。つまり $_SESSION の中身は変わりません。
<?php
//page1.php
session_start();
$_SESSION['animal']='dog';
print session_id().'<br>';
print '<a href="page2.php">page 2</1>';
?>
<?php
//page2.php
session_start();
session_regenerate_id(true);
print session_id().'<br>';
print $_SESSION['animal'];
?>