【PHP超入門】Cookieとセッションについて

  • 148
    いいね
  • 1
    コメント

はじめに

PHPでメールフォームや掲示板などWebアプリケーションを作りたいと考えている方は、Cookieとセッションについて必ず理解しておく必要があります。
Cookieとセッションは、ユーザーを識別するために欠かせない技術で、非常に重要な役割をしております。
ここを理解すれば、作りたいWebアプリケーションに一歩前進しますので、Cookieとセッションについて理解しましょう。

私自身、未熟ですので説明に誤りがあるかもしれません。
そのときは、ご指摘ください((_ _ (´ω` )ペコ
この記事は、HTTPとHTMLとPHPの基礎知識のある方が対象です。
特にHTTPを理解しておく必要があります。
リクエスト?レスポンス?という方は、私が投稿した

をご一読ください。
理解してないと序章から意味不明です。
上記の記事を読めば、この記事も理解できると思います。

今回、Cookieとセッションについて学ぶために

という流れでまとめました。
今回、HTTPメッセージの確認は、Fiddler を使用しています。
PHPをローカル環境でも実行できるように、Microsoftで提供している WebMatrix を導入し、ブラウザはGoogleChromeで確認しております。

動作環境

OS サーバー PHP GoogleChrome
Windows10 Microsoft-IIS/8.0 5.5.38 52.0.2743.116

序章

HTTPは、リクエストし、レスポンスする、この1往復で通信が切断されます。

http-stateless.png

ご覧の通り、1往復で通信が完了しているため、サーバーは、前のページからの続きなのか、関連したアクセスなのか、以前の状態を全く覚えていません。
これを ステートレス(stateless) と言います。
ステート(state)とは状態という意味で、レス(less)が付くので、状態が無いという意味になります。

以前の状態を覚えていないと、Amazonのような買い物できるWebサイトは作れません。
ネットショッピングをするのにログインし、商品をカートに入れ、購入します。
ページ遷移するたびに、通信は切断されています。
サーバーは、誰がカートに入れ、購入したのかを全く覚えていません。
これでは、非常に困ります。
状態を保持(ステートフル)にするには、どのようにすればいいでしょうか。
答えは単純で、サーバーがクライアントに対して識別できる一意の値を渡し、クライアントはリクエストするときに、その値を教えてあげれば、サーバー側で識別できるようになり、それぞれの状態を覚えることができます。
その役割をしているのが、Cookie(クッキー)です。

次は、Cookieについて見ていきましょう。

第1章 Cookieについて

Cookieって何?

正式には HTTP Cookie という名称ですが、一般的には略してCookieと呼びます。
Cookieは、クライアントに保存された情報のことです

サーバーからのレスポンスメッセージに「Cookieを保存してください」という指示があれば、クライアントは指示に従い、Cookieを保存します。
クライアントにCookieが保存されていれば、クライアントは、常にリクエストメッセージに「このようなCookieがあります」とサーバーに教える仕組みになっています。

cookie.png

具体的に言うと、サーバーは、レスポンスメッセージに Set-Cookie ヘッダを付与することで、クライアントに対して「Cookieを保存してください」と指示することが可能です。
Set-Cookie ヘッダには、Cookieの有効期限やCookieを送るドメイン、有効パスなどの各属性があり、; で区切って記述することで設定が可能です。
Set-Cookie ヘッダの各属性については、後ほど確認しますので、ここでは Set-Cookie ヘッダに各属性を記述することで、設定できると覚えておいてください。

レスポンスメッセージ(Set-Cookieヘッダの構文)
Set-Cookie: 名前=値;[expires=有効期間(日時)];[Path=URL相対パス];[Domain=ドメイン名];[secure];[httponly]
レスポンスメッセージ(Set-Cookieヘッダの記述例)
Set-Cookie: a=b; expires=Mon, 15-Aug-2016 12:00:00 GMT; path=/; domain=xxxx.com; secure; httponly

サーバーからの Set-Cookie ヘッダの内容に従い、クライアントはCookieを保存します。
逆にクライアントは、リクエストメッセージに Cookie ヘッダを付与することで、サーバーに対して「このようなCookieが保存されています」と教えることができます。
Cookieが存在する限り、常にリクエストメッセージに Cookie ヘッダを付与します。

リクエストメッセージ(Cookieヘッダの構文)
Cookie: 名前=値
リクエストメッセージ(Cookieヘッダの記述例)
Cookie: a=b

最初に、Cookieは、クライアントに保存された情報と説明しましたが、人によって捉え方や指している意味が異なる場合があります。
Cookieというだけで、仕組み全体を指す場合もありますので、臨機応変に解釈してください。
この記事では、Cookieとはクライアントに保存された情報という意味で使用します。

Set-Cookie ヘッダの各属性を確認する前に、Cookieの歴史を見ていきましょう。
Cookieの仕様は、紆余曲折あり、その経緯を把握することで、理解が深まります。

Cookieの仕様と歴史

Cookieは、1994年にNetscape Communications(以下、Netscape)というアメリカの会社によって、考案・実装されました。
Netscapeは、1998年にAOLによって買収されたため、聞いたことない方が多いかと思います。
Netscapeは、1994年にNetscape Navigator(ネットスケープ ナビゲーター)というブラウザをリリースしました。
その後、マイクロソフトとブラウザのシェア争いをしましたが、シェア率は下がり、2008年に全バージョンのサポートを終了しました。
Netscapeは、Cookieの他に、JavaScript、RDF/RSS、 SSL といったWebに関する技術を生み出した会社です。
Webに従事する方なら、今後もNetscapeという名前は出てくるかと思いますので、今のうちに覚えておきましょう。

Cookieの仕様は、Netscapeが実装したものがベースとなっております。
その後、RFCで策定と廃止を繰り返し、最新の仕様はRFC6265です。

  • 1994年 Netscape
  • 1997年 RFC2109
  • 2000年 RFC2965
  • 2011年 RFC6265

各ブラウザがNetscape仕様に従っていたため、RFC2109、RFC2965で変更・追加された属性が、ほとんどのブラウザに実装されず、RFCの仕様とブラウザの実装が乖離していました。
どのような経緯で今のRFC6265になったのか簡単に確認して理解を深めましょう。

最初のNetscape仕様と比較して追加・変更されたヘッダ・属性は、太字で記載しております。
また、各属性の意味がわからないと思うので、簡単な説明を記載しております。
後ほど、各属性については詳しく説明しますので、ここでは、どのような属性が変更・追加・削除されたのかに注目しましょう。

1994年 Netscape

cookie-netscape.png

  • 1994年にNetscapeによって考案・実装されました。
  • RFCのベースとなる仕様で、他のブラウザもこの仕様をもとに実装されました。
  • Name属性の記述は必須です。

1997年 RFC2109

cookie-rfc2109.png

  • 1997年2月にRFC2109として公開。RFCとして初めての仕様です。
  • Netscape仕様の Expires 属性が Max-Age 属性に変更されたため、互換性はありません。
  • 新たに Comment 属性 と Version 属性が追加されました。
  • Name 属性 と Version 属性は必須です。

2000年 RFC2965

cookie-rfc2965.png

  • 2000年10月にRFC2965として公開され、前のRFC2109は、廃止されました。
  • レスポンスメッセージのヘッダが Set-Cookie2になり、リクエストメッセージのヘッダが Cookie2 に変更され、前回のRFC2109からも大幅に変更されました。
  • RFC2109で変更・追加された属性はそのままで、さらに CommentURL 属性と Discard 属性と Port 属性が追加されました。
  • RFC2109と同じく Name 属性 と Version 属性は必須です。

2011年 RFC6265

cookie-rfc6265.png

  • 前の仕様から約11年後の、2011年4月にRFC6265として公開。前のRFC2965は廃止されました。
  • 約11年の歳月がありましたが、RFC2109、RFC2965で追加された属性は 、ほとんどのブラウザで実装されなかったため、Max-Age 属性を除き、全て削除されました。
  • Netscape仕様にあった Expires 属性がRFCとして初めて追加されました。
  • 新たに HttpOnly 属性が追加されましたが、もともとInternetExplorerが独自に追加(InternetExplorer 6 / SP1以上)していた属性で、他の主要ブラウザでも実装済みだったのが、正式にRFCとして追加されました。

今までの経緯を見るとわかりますが、RFCで追加された属性は、ほとんどのブラウザで実装されなかったので、逆にRFCがブラウザの実装にあわせて作った仕様がRFC6265です。
過去の仕様も確認することで、どのような経緯でRFC6265になったか理解できたかと思います。
次は、RFC6265の Set-Cookie ヘッダの各属性について詳しくみていきましょう。

Set-Cookieヘッダの属性

先ほども記載しましたが、下記のように Set-Cookie ヘッダを記述することで各属性の設定が可能です。

レスポンスメッセージ(Set-Cookieヘッダの構文)
Set-Cookie: 名前=値;[expires=有効期間(日時)];[Path=URL相対パス];[Domain=ドメイン名];[secure];[httponly]

各属性は下記の通りです。

http-setcookie.png

expires属性とMax-Age属性の違いは?

上表の通り、expires 属性と Max-Age 属性は指定方法が違うだけで同じ役割をしています。
なぜ、同じ役割をする属性が2つ存在するかは、先ほどの経緯を見ればわかります。
元々、Netscape仕様では expires 属性のみでしたが、その後のRFC2109を策定するときに expires 属性を Max-Age 属性に変更しました。
ただ、各ブラウザはNetscape仕様をもとにしており、Max-Age 属性に対応していないブラウザがあります。
InternetExplorer11は、expires 属性に対応しておりますが、Max-Age 属性には対応しておりません。
下記の検証を見ると、Max-Age 属性に対応していないのがわかります。

そうなると、Max-Age 属性の存在意義は?と疑問に思いますが、Max-Age 属性は、最初のRFCから登場しており、今さら Max-Age 属性を削除するわけにもいかず、RFCとしてはMax-Age属性を推しているのかと思います。
(※RFC6265の策定時にどのような経緯で残したのかまで把握しておりませんので、あくまで私の推察です。)
ただ、主要ブラウザであるInternetExplorer11でも Max-Age 属性に対応していないため、expires 属性で指定するのが一般的です。
どちらの属性で指定するのが多いのか、QiitaやGoogle、AmazonなどのWebサイトのHTTPメッセージを確認しましたが、expires 属性で指定しており、Max-Age 属性の記述は見当たりませんでした。
私の調べ方に誤りがあるかもしれません。そのときは、ご指摘ください((_ _ (´ω` )ペコ

Cookieは何個も作成できるの?

1つのドメインに対して、作成できるCookieの数とサイズに上限があります。
実際は、各ブラウザごとに異なりますが、MicrosoftのWebページをもとに確認してみましょう。

Microsoft Internet Explorer は、次の RFC 2109推奨の最低限の制限に準拠しています。

  • 少なくとも 300 のクッキー
  • (Set-cookie ヘッダーの構文の説明ではターミナル以外の cookie を構成する文字のサイズを測定する) ように cookie あたり 4096 バイト以上
  • 一意なホスト名またはドメイン名ごとに少なくとも 20 の cookie

引用元:Internet Explorer 内の cookie の数とサイズの制限

少しわかりにくいですが、ブラウザ全体で300個のCookieに対応し、1つのドメインあたり、20個のCookie(1つのCookieは4096バイト)を提供できますと記載されています。
これは、Netscape、RFC2109、RFC2965に準拠した数とサイズです。
逆に言えば、Netscapeの仕様から変更がなかったため、それぞれの仕様に準拠したという捉え方もできます。

その後、InternetExplorerでは、下記の変更がありました。

更新プログラム 937143 をインストールすると、Internet Explorer で保存できるドメインあたりの Cookie の数が 20 から 50 に増えます。

引用元:Internet Explorer で、ドメインあたりの Cookie の数の制限が 20 から 50 に増加する

これは、最新の仕様であるRFC6265に準拠しています。
RFC6265では下記のように記載があります。

6.1. 制限

実施上、 UA の実装は,格納­可能なクッキーの総数とサイズを制限する。 汎用の UA は、少なくとも次に示す容量は提供するべきである:
1クッキーあたり,少なくとも 4096 バイト(クッキーの名前, 値, 属性 の長さの総和で)
1ドメインあたり,少なくとも 50 個のクッキー
全部で少なくとも 3000 個のクッキー

引用元:RFC6265 — HTTP State Management Mechanism (クッキー処理仕様)日本語訳

ご覧の通り、50個と記載されていますが、Cookieが20から50に増加したという記事の最新更新日は2007年3月9日です。
RFC6265の公開は2011年です。
InternetExplorer が RFC6265 の仕様に準拠したわけではなく、たまたま同じ数になった、もしくは RFC6265 が InternetExplorer の数にあわせただけです。

実際は、各ブラウザごとに実装が異なるというのが現状です。
意図した動作をしないときはブラウザごとに仕様を調べてください。

Cookieの数とサイズに関しては、下記の調査結果が参考になります。

保存できるサイズに上限があるため、Cookieに様々な情報を保存するという使い方はできません。
また、リクエストメッセージは、偽装可能です。
Cookieもリクエストメッセージで送られてきますので、信用できない情報です。
そのため、変更されて困る情報を、Cookieで扱ってはいけません
例えば、商品名や価格をCookieに保存して、その情報を利用して買い物ができるシステムを構築することも可能ですが、リクエストメッセージとして送られてくるCookieは、偽装可能なため、価格を変更して買い物することができてしまいます。
このような欠陥を防ぐためにも、変更されて困る情報は、Cookieで扱わないようにしましょう。

次は、自分のパソコンに保存されているCookieを実際に確認してみましょう。

自分のパソコンに保存されているCookieを確認

Cookieはブラウザごとに保存されています。
GoogleChromeを使っている方は、URLに chrome://settings/cookiesを入力すると保存されているCookieの一覧が表示されます。

cookie-local.png

上図のようにCookieに関する情報が表示されます。
Set-Cookieで定義された名前やドメイン、有効期限などの情報も確認できます。
Cookieには、ログインに関する情報も含まれていることがあります。
安易に人に教えたり、ネット上に掲載しないようにしてください。

次は、PHPを使ってSet-Cookieを定義してみましょう。

PHPでSet-Cookieヘッダを定義する

Set-Cookie ヘッダを定義するには、setcookie 関数を使います。
setcookie 関数は、第7引数まであり、各設定が行えます。
各設定というのは、先ほど確認した Set-Cookie ヘッダの各属性のことです。

PHP(setcookieの構文)
setcookie(Cookie名, Cookie値, 有効日時, パス, ドメイン, HTTPS接続のみ, Javascript無効)
PHP(setcookieの記述例)
setcookie('a', 'b', time()+60*60, '/', 'xxxxx.com', true, true

php-setcookie.png

引用元:PHP: setcookie - Manual

引数に設定する項目は、先ほどの Set-Cookie ヘッダの属性と同じなので、説明がなくても理解できると思います。
ただ、有効期限の指定方法が異なります。
Set-Cookie ヘッダのところでは、Wdy, DD-Mon-YYYY HH:MM:SS GMTというフォーマットで指定してくださいと説明しましたが、phpが内部で自動的に変換してくれます。
上述した説明の通り、time() 関数を使って指定してください。
また、Set-Cookie ヘッダにあった Max-Age 属性がありません。
PHP5.5.0から第3引数を指定すれば、expires 属性の他に Max-Age 属性も指定してくれます。

5.5.0 クライアントに送出する Set-Cookie ヘッダに、Max-Age 属性も含めるようになりました。

Cookieを削除するには、有効期限を過去の時間にしてください。
また、Cookieをセットしたときと同じパス、ドメインを指定してください。

PHP(Cookie削除の記述例)
setcookie('a', 'b', time()-1, '/', 'xxxxx.com', true, true

次は、PHPリクエストメッセージにあるCookieを取得する方法を見ていきましょう。

PHPでCookieを取得する方法

リクエストメッセージのCookieヘッダにある値は、スーバーグローバル変数の $_COOKIE に連想配列として格納されています。

難しそうに聞こえますが、大したことありません。

リクエストメッセージに下記のCookieヘッダがあったと仮定します。

リクエストメッセージ(Cookieヘッダ)
Cookie: name=sato

先ほどのCookieヘッダの構文で記載しておりましたが、 name=satoというのは、名前=値という書き方をしており、nameが名前でsatoが値になります。

このname=satoを、どこからでも読み込みできる変数に配列として格納しているだけのことです。
要は下記のように $_COOKIE という変数に配列として格納しているだけです。

PHP
$_COOKIE = array(
    'name' => 'sato';
)

上述した配列は、キーの部分が name という文字になっておりますので、正確には連想配列と呼びます。
連想配列に格納された値を取得するときは $変数名[キー] のように記述します。
スーパーグローバル変数の $_COOKIE に格納されている name という名前の値を $name という変数に代入し、画面に出力するときは下記のように記述します。

PHP
$name = $_COOKIE['name'];
echo $name;

画面には、satoと表示されます。

このサンプルでは、Cookieの値が問題ないかチェックして、表示するときにエスケープするということをしておりません。
リクエストメッセージは偽装可能なため、実際はチェックしてエスケープする必要があります。

値の受け取り方は、@mpyw さんの

が参考になるので、必ずご覧ください。

これでCookieの取得方法は理解できたかと思います。
次は、Cookieという名前について見ていきましょう。

Cookieという名前の由来は?

今ままで普通にCookieと呼んでいましたが、なぜCookieという名前なのか気になるかと思います。
普通は、Cookieと言われて、連想するのは、糖分や脂肪分の割合が40%以上で、かつ、手作り風の外観を有しているお菓子のことかと思います。
もちろん、今回扱っているCookieは、ビスケットの一種であるクッキーのことを指しているわけではありません。

では、なぜクライアントに保存されるファイルまたはデータのことをCookieと呼ぶようになったのでしょうか。
Cookieを考案したNetscapeでブラウザを開発していたルー・モントゥリ氏がブログでCookieの由来について言及しているのが有力な説のようです。

OSの一つであるUNIXで HTTP Cookie と多少似たような動きする magic cookie というのがあります。
ルー・モントゥリ氏は、大学の授業で maginc cookie という用語を聞いており、それを参考に命名したとのことです。
また、英語版Wikipediaの HTTP cookie には下記の記述があります。

Origin of the name
The term "cookie" was coined by web browser programmer Lou Montulli. It was derived from the term "magic cookie", which is a packet of data a program receives and sends back unchanged, used by Unix programmers. Magic cookie in turn derives from "fortune cookie", a cookie with an embedded message.[citation needed]

HTTP cookie の名前の由来となった magic cookie は、フォーチュンクッキーというお菓子が名前の由来ということのようです。

フォーチュン・クッキーまたはおみくじクッキー (fortune cookie) とは、その中に運勢が表記されている紙片(おみくじ)が入っている菓子である。アメリカ合衆国・カナダの中華料理店において食後に提供されることが多い。

元をただせば、おみくじが入ったクッキーが名前の由来だったみたいですね。
あくまで有力な説であり、名前の由来は諸説あります。

他人のCookieを盗んで悪戯しても問題ないの?

Cookieには、ログインに関する重要な情報が含めれていることがあります。
好奇心で他人のCookieを盗んで悪戯してみようと考える方もいるかもしれませんが、不正アクセス禁止法で逮捕されます。
他人の情報を不正に取得するのは犯罪です。

「不正アクセス禁止法」を犯した際の罰則は、3年以下の懲役、または100万円以下の罰金です。逮捕され、裁判にかけられ、刑罰が与えられ、社会的な信頼が揺らぎ、前科もつく。時効は、刑事訴訟法第250条に基づき3年です。

引用元: 【事例つき】他人のSNSを勝手に操作!?「不正アクセス禁止法」で犯罪者にならないために

逮捕されるので、悪戯しないでください。
次はセッションについて確認しましょう。

第2章 セッションについて

セッションって何?

セッションとは、一連の処理の始まりから終わりまでを表す概念のことです。
セッションという定義は広く、さまざまなところでセッションという用語が使われます。

Wikipediaの セッション を見るとわかりますが、下記の通り様々なところでセッションという用語が使われています。
理解できない用語も多々あるかと思いますが、セッションというのがどのような意味で使われているか確認してください。

  • セッション (コンピュータ)(英語版) - コンピュータの用語で、一連のインタラクティブな操作のこと。典型的にはログイン(ログイン)してからログアウト(ログオフ)するまでが一つのログインセッション(英語版)。
  • コンピュータネットワーク用語で、一連の通信のこと。典型的には接続を確立してから切断するまでが一つのセッション。HTTPのセッションは「セッションID」を使って同一のセッションを管理する。セッションIDはHTTP cookieに保存してHTTPヘッダに格納して送受信したり、URLに埋め込んでGETPメソッドで送受信したりする。
  • セッションハイジャック - 通信当事者以外がセッションを乗っ取る行為。
  • セッション層 - OSI参照モデルの第5層。アプリケーション・プロセス間でセッションの開始、終了、管理の機構、すなわち半永続的な対話を提供する。
  • セッション (ウェブ解析)(英語版) CDにおいて、リードインに始まりリードアウトに終わる一連のデータのこと。CD EXTRAや追記したCD-Rでは複数のセッションが存在する(マルチセッション)。

共通点は一連の処理の始まりから終わりまでを表しているということです。
今回は、2番目に記載されているHTTPセッションについて学びます。

例えば、ネットショッピングの場合、ログインし、商品をカートに入れ、購入まで一連の処理(セッション)として扱いたいですが、序章で説明したとおり、HTTPはステートレスのため、一連の処理(セッション)として扱えません。
そこで第1章で学んだCookieを利用して、一連の処理(セッション)として扱います。
具体的には、Cookieに一意の値を入れ、リクエストするときにCookieにある値も一緒に送ってもらうことで、識別可能になり、一連の処理として扱えるようになります。
Cookieを利用することで、セッション管理が行えるということです。

リクエストするときに識別可能な値を入れてもらうだけなので、Cookieを使わずに、リンクのURLに値を記述してリクエストしてもらったり、フォームに値を記述してリクエストしてもらうことで、セッション管理することも可能です。
ただ、他の方法は、Cookieを使う方法と比べて、情報が漏洩する可能性が高いので、Cookieを使ってセッション管理するのが一般的です。
そのため、Cookie以外でセッション管理する方法を、ここでは詳しく説明しません。
興味のある方は、調べてください。

PHPでセッションを管理する

今さら、説明は不要かと思いますが、PHPはサーバーサイドスクリプトです。
PHPはサーバー側で実行されます。
これからの話は全てサーバー側の話になります。
この前提を忘れないでください。

PHPでセッションを開始するには session_start() 関数を利用します。

PHP
session_start();

session_start()を記述すると、セッションが開始されます。
先ほど、セッションを管理するには、Cookieに一意の値を入れて、リクエストしてもらうことで、一連の処理として扱えると説明しました。
この一意の値の役割をするのがセッションIDです。
session_start()を記述だけで、自動でセッションIDを発行してくれます。
セッションIDを発行するときに、セッションIDごとにセッションファイルを作成します。
このセッションファイルは、サーバーに作成されます。
作成されたセッションファイルには、任意の値を入れることが可能です。
session_start()は、リクエストメッセージにセッションIDがあるのか、ないのかで挙動が変わります。
まとめると下記のような動作をします。

リクエストメッセージにセッションIDがない場合

  • セッションIDがなければ、セッションIDを発行し、セッションIDをCookieに保存するようにレスポンスメッセージを送信
  • セッションIDを発行するときに、サーバー側では、セッションIDごとにセッションファイルを作成

リクエストメッセージにセッションIDがある場合

  • セッションIDがあれば、該当するセッションファイルにある情報を参照

ご覧のようにsession_start()はセッションIDがあるか、ないかを判断してくれます。
セッション管理するページでは、session_start()を必ず記述する必要があります。
また、HTMLタグより先に記述する必要があります。
下記のように先に記述してください。

PHP
<?php
session_start();
?>
<html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
</body>
</html>

次は、Cookieを使ってどのようにセッション管理をしてるのか見ていきましょう。

Cookieを使ってどのようにセッション管理してるの?

まだ、セッションファイルに情報を保存する方法、取得する方法を説明していませんが、先にどのような流れでセッション管理しているか確認してみましょう。

初回のアクセス

session-first.png

クライアントは、session_start() と記述されたphpファイルへアクセスします。
リクエストを受け、サーバーでは session_start() を実行し、セッションIDを発行します。
セッションIDを発行するときに、セッションファイルを作成します。
セッションIDをCookieに保存するようにレスポンスします。
クライアントは、セッションIDをCookieに保存します。

次のアクセスで、セッション情報を保存してみましょう。

2回目のアクセス

session-second.png

クライアントは、session_start() と記述されたphpファイルへアクセスします。
リクエストするときにセッション情報に保存してくださいと指示することが可能です。
今回は、セッション情報に name というキーに 佐藤 という値を保存してくださいと指示しました。
サーバーは、該当するセッションファイルにリクエストされた情報を保存します。

次のアクセスでは、セッションファイルにある情報を表示してみましょう。

3回目のアクセス

session-third.png

クライアントは、session_start() と記述されたphpファイルへアクセスします。
リクエストするときにセッションファイルにある name の値をくださいと指示をします。
リクエストの指示に従い、 name の値である佐藤という値をレスポンスします。

これでどのようにセッション管理しているのか理解できたかと思います。
また、セッション情報は、サーバー側に保存されるため、Cookieのように偽装されることはありません。
そのため、偽装されて困るデータは、クライアント側(Cookie)ではなくサーバー側(セッションファイル)に保存するようにしましょう。

先ほど、session_start() を記述すれば、自動でセッションIDを発行してくれると説明しました。
次は、このセッションIDについて確認しましょう。

セッションIDってどのような値でもいいの?

セッションIDは、個人を識別するために使われる重要な値です。
先ほどの図で言うと、セッションIDの値は e89r86p6dg04fumh0lanro3pn5 です。
この値が単純だったり、推測できる値だと、個人を識別する値としては使えません。
PHPの場合は、自動的に複雑な値を作成してくれます。
複雑な値って文字列を並べるだけでは?と思いますが、推測できない一意の値にしなければいけないので、そんな単純な話ではありません。
複雑な値の作り方は、いくつか種類があります。
私の初期環境(PHP 5.5.38)では「MD5」という方法で複雑な値を作成しておりますが、もっと複雑な値を作成できる種類もあります。
この初心者向けの記事を読んでいる方で、決済など絡むWebアプリケーションを作成する方はいないと思いますが、そのようなWebアプリケーションを作成するときは、この種類を変更してください。
PHPの設定が記述されている php.ini というファイル、または ini_set関数を使って session.hash_function を設定することで、種類の変更が可能です。
PHPのバージョンによって設定できる種類が異なります。
どのような種類があるのかなど、各自で調べてください。
ここでは、複雑な値を作成するのにいくつか種類があるということ、決済など絡む場合は、この種類について考慮する必要があるということを覚えておいてください。

次は、サーバー側に保存されているセッション情報を見ていきましょう。

セッションファイルの保存先

私と同じローカル環境(WebMatrix)なら、セッションファイルの保存先は C:\WINDOWS\temp です。
セッションファイルの保存先が不明なときは、phpinfo()を記述したPHPファイルを実行して確認できます。
session.save_pathという項目にセッションファイルの保存先が記載されておりますので、保存先が不明な方はそちらをご覧ください。

先ほどの図にもありましたが、保存先を見ると sess_xxxxxxxxxxxxxxxxxxxxxxxxxx という名前のセッションファイルが作成されます。
xxxxxxxxxxxxxxxxxxxxxxxxxx はセッションIDです。
このセッションファイルの中にセッション情報が保存されています。

session-local.png

ご覧のようにセッションIDごとにセッションファイルが作成され、このセッションファイルに情報を保存することが可能です。

共有サーバーを利用している場合

セッションファイルの保存先は、 temp フォルダに保存されるのが一般的です。
共有サーバーを利用している方は、保存先の temp フォルダの閲覧ができないことがあります。
また、他のユーザーも同じ保存先だった場合、影響を受けることがありますので、保存先を変更してください。
PHPなら、session_save_path 関数を使えば、保存先を変更できます。

PHPでセッションの情報を保存・取得する方法

ここでは省略しますが、セッションを利用するので、最初に session_start() を記述することを忘れないでください。
セッション情報は、スーパーグローバル変数の $_SESSION に連想配列として格納されます。
先ほどの $_COOKIE と同じなので、意味は理解できると思います。

$_SESSION の連想配列に値を追加するには、下記のように記述します。

PHP
$_SESSION['名前'] = 

名前をnameにして、佐藤という値を格納するには、下記のように記述します。

PHP
$_SESSION['name'] = '佐藤';

これでセッションファイルに情報を保存できます。
次は、保存した情報を取得する方法ですが、Cookieのときと同様です。

スーパーグローバル変数の $_SESSION に格納されている name という名前の値を $name という変数に代入し、画面に出力するときは下記のように記述します。

PHP
$name = $_SESSION['name'];
echo $name;

画面には、佐藤と表示されます。

これでセッション情報の保存と取得方法は、理解できたかと思います。
次は、セッションファイルの有効期限を変更する方法を見ていきましょう。

PHPでセッションファイルの有効期限を変更する方法

セッションファイルの有効期限を設定するには、session.gc_maxlifetime を変更します。
php.iniまたはini_set関数で変更することが可能です。
session.gc_maxlifetimeの初期値を確認するには、phpinfo()を実行することで確認できます。
私の初期環境(PHP5.5.38)では、session.gc_maxlifetimeの値は1440です。
1440は秒数ですので、24分間が有効期限ということです。
ini_set関数を使って有効期限を1日に変更するには、下記のように記述します。
この変更は session_start() より前に記述してください。

PHP
ini_set('session.gc_maxlifetime', 60 * 60 * 24);

初期値では、24分間が有効期限と説明しましたが、最終アクセスから数えて24分後に削除されます。
ただ、すぐには削除されません。
session.gc_divisorの値を分母とし、session.gc_probabilityの値を分子とした確率で削除されます。
かなり難しそうに聞こえますが、大したことありません。
私の初期環境(PHP5.5.38)では、session.gc_divisorの値は1000となっており、session.gc_probabilityの値は1になっております。
値の通り、1000回に1回の確率で削除されるということです。
有効期限が過ぎたセッションファイルは、削除可能な状態で待機し、1000回に1回の確率でまとめて削除されるという仕組みになっています。
少しややこしい仕組みですが、毎回削除するとサーバーに負荷がかかるため、1000回に1回まとめて削除するようです。
このsession.gc_divisorsession.gc_probabilityの値もphp.iniまたはini_set関数で変更することが可能です。

実際には、もう少し複雑です。
詳細は、@mpyw さんの

が参考になるので、そちらも必ずご一読ください。

PHPでセッションファイルを破棄する方法

特定のセッション情報を削除するには、unset()関数を使用します。

PHP
unset($_SESSION['name']);

セッションファイルを破棄するには、下記のように記述します。

PHP
$_SESSION = array();
setcookie(session_name(), '', time()-1, '/');
session_destroy();

$_SESSIONは、連想配列で値が格納されていますので、空の配列 array() を代入すれば、セッション情報は破棄されます。
最後にsession_destroy()関数を使って、セッションファイルを削除しています。
セッションファイルは破棄されましたが、クライアントにあるCookieにはセッションIDがあります。
Cookieを削除するには、先ほど説明したようにsetcookie()関数に過去の時間を指定して削除してください。
また、同じパスとドメインを指定してください。
Cookieを削除するときに session_name() とありますが、これはセッション名を取得する関数です。
session_start() を記述すると自動でセッションIDが作成されると説明しました。
初期のセッション名は、PHPSESSID です。
要は、Cookieの名前が PHPSESSID で、値が自動で作成したセッションIDということです。
セッション名を変更していないなら、session_name()と記載すると PHPSESSID というセッション名を取得します。

これでセッション管理について一通り理解できたかと思います。

最後に

この記事は、Cookieとセッションの基礎しか記載しておりません。
もともと、@mpyw さんの

を理解するために調べた内容です。
PHPでCookieやセッションを扱うには、他に留意すべき点があります。
上述したリンク先のページに記載されておりますので、必ずご一読ください。

また、Cookieとセッションが、Webアプリケーションにおいて重要な役割をしております。
このセション管理に不備があると、悪用されてしまう危険性があります。
セッションに関連する脆弱性でも下記のように様々な種類があります。

Webアプリケーションを作る上で、このような脆弱性を理解して、対策する必要があります。
セキュリティ対策について体系的に学びたいなら、徳丸さんの著書

が参考になると思います。
HTTP、Cookie、セッション、PHPについて基本的なことを理解していれば、何が原因でどのような対策すればよいのか理解できるかと思います。
偉そうなことを言ってますが、私もセキュリティについての理解が乏しいです(´ω`;)
これからも研鑽したいと思います。
今回の記事で超初心者の理解が少しでも深まれば幸いです。
最後まで読んでいただき、ありがとうございました。