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

# Udemy で講座の販売を開始しました！

Udemy で「プログラミング学習の心得＆HTTPの基礎」の動画講座を公開しました。
特別に 85% OFFのクーポンを発行します。
通常価格 10,800 円が 1,800 円で購入できます。
動画内でも説明していますが、初心者・初学者向けの内容です。
事前に自分の知りたい内容・興味のある内容か確認してからご購入ください。

- [挫折した人が教える PHP プログラミングシリーズ Vol.1『学習の歩き方』](https://www.udemy.com/course/learn-php-for-beginners-vol1/?couponCode=9DA8AF9EBBE3AA414B64)

# **はじめに**

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

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

+ [Qiita -【PHP超入門】HTTP（GET・POST）について](http://qiita.com/7968/items/4bf4d6f28284146c288f) by @7968

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

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

* [序章](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E5%BA%8F%E7%AB%A0)
* [第1章 Cookieについて](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E7%AC%AC1%E7%AB%A0-cookie%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
　[Cookieって何？]
(http://qiita.com/7968/items/ce03feb17c8eaa6e4672#cookie%E3%81%A3%E3%81%A6%E4%BD%95)
　[Cookieの仕様と歴史](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#cookie%E3%81%AE%E4%BB%95%E6%A7%98%E3%81%A8%E6%AD%B4%E5%8F%B2)
　[Set-Cookieヘッダの属性](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#set-cookie%E3%83%98%E3%83%83%E3%83%80%E3%81%AE%E5%B1%9E%E6%80%A7)
　[Cookieは何個も作成できるの？](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#cookie%E3%81%AF%E4%BD%95%E5%80%8B%E3%82%82%E4%BD%9C%E6%88%90%E3%81%A7%E3%81%8D%E3%82%8B%E3%81%AE)
　[自分のパソコンに保存されているCookieを確認](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E8%87%AA%E5%88%86%E3%81%AE%E3%83%91%E3%82%BD%E3%82%B3%E3%83%B3%E3%81%AB%E4%BF%9D%E5%AD%98%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8Bcookie%E3%82%92%E7%A2%BA%E8%AA%8D)
　[PHPでSet-Cookieヘッダを定義する](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#php%E3%81%A7set-cookie%E3%83%98%E3%83%83%E3%83%80%E3%82%92%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B)
　[PHPでCookieを取得する方法](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#php%E3%81%A7cookie%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
　[Cookieという名前の由来は？](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#cookie%E3%81%A8%E3%81%84%E3%81%86%E5%90%8D%E5%89%8D%E3%81%AE%E7%94%B1%E6%9D%A5%E3%81%AF)
　[他人のCookieを盗んで悪戯しても問題ないの？](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E4%BB%96%E4%BA%BA%E3%81%AEcookie%E3%82%92%E7%9B%97%E3%82%93%E3%81%A7%E6%82%AA%E6%88%AF%E3%81%97%E3%81%A6%E3%82%82%E5%95%8F%E9%A1%8C%E3%81%AA%E3%81%84%E3%81%AE)

* [第2章 セッションについて](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E7%AC%AC2%E7%AB%A0-%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
　[セッションって何？](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A3%E3%81%A6%E4%BD%95)
　[PHPでセッションを管理する](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#php%E3%81%A7%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B)
　[Cookieを使ってどのようにセッション管理してるの？](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#cookie%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86%E3%81%97%E3%81%A6%E3%82%8B%E3%81%AE)
　[セッションIDってどのような値でもいいの？](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3id%E3%81%A3%E3%81%A6%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E5%80%A4%E3%81%A7%E3%82%82%E3%81%84%E3%81%84%E3%81%AE)
　[セッションファイルの保存先](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BF%9D%E5%AD%98%E5%85%88)
　[PHPでセッションの情報を保存・取得する方法](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#php%E3%81%A7%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E6%83%85%E5%A0%B1%E3%82%92%E4%BF%9D%E5%AD%98%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
　[PHPでセッションファイルの有効期限を変更する方法](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#php%E3%81%A7%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E6%9C%89%E5%8A%B9%E6%9C%9F%E9%99%90%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
　[PHPでセッションファイルを破棄する方法](http://qiita.com/7968/items/ce03feb17c8eaa6e4672#php%E3%81%A7%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E7%A0%B4%E6%A3%84%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)

という流れでまとめました。
今回、HTTPメッセージの確認は、[Fiddler](https://www.telerik.com/download/fiddler) を使用しています。
PHPをローカル環境でも実行できるように、Microsoftで提供している [WebMatrix](https://www.microsoft.com/japan/web/webmatrix/) を導入し、ブラウザはGoogleChromeで確認しております。

### **動作環境**

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

# **序章**

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

![http-stateless.png](https://qiita-image-store.s3.amazonaws.com/0/72985/796a80fd-f9d6-c72e-99ec-31ef7e08b156.png)

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

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

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

# **第1章 Cookieについて**

## **Cookieって何？**

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

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

![cookie.png](https://qiita-image-store.s3.amazonaws.com/0/72985/860833f1-6efb-161c-9a76-7828201681f3.png)

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

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

```txt:レスポンスメッセージ（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` ヘッダを付与します。

```txt:リクエストメッセージ（Cookieヘッダの構文）
Cookie: 名前=値
```

```txt:リクエストメッセージ（Cookieヘッダの記述例）
Cookie: a=b
```

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

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

## **Cookieの仕様と歴史**

Cookieは、1994年に[Netscape Communications（以下、Netscape）](https://ja.wikipedia.org/wiki/%E3%83%8D%E3%83%83%E3%83%88%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%97%E3%82%B3%E3%83%9F%E3%83%A5%E3%83%8B%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%BA)というアメリカの会社によって、考案・実装されました。
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](https://qiita-image-store.s3.amazonaws.com/0/72985/6b2f0707-2a1f-4e2f-b120-ccc0cb4ffdf5.png)

+ 1994年に[Netscape](https://curl.haxx.se/rfc/cookie_spec.html)によって考案・実装されました。
+ RFCのベースとなる仕様で、他のブラウザもこの仕様をもとに実装されました。
+ `Name`属性の記述は必須です。

### **1997年 RFC2109**

![cookie-rfc2109.png](https://qiita-image-store.s3.amazonaws.com/0/72985/ed8e4213-41ae-0a0a-d5b3-7b1d5d7e4379.png)

+ 1997年2月に[RFC2109](https://www.ietf.org/rfc/rfc2109.txt)として公開。RFCとして初めての仕様です。
+ Netscape仕様の `Expires` 属性が `Max-Age` 属性に変更されたため、互換性はありません。
+ 新たに `Comment` 属性 と `Version` 属性が追加されました。
+ `Name` 属性 と `Version` 属性は必須です。

### **2000年 RFC2965**

![cookie-rfc2965.png](https://qiita-image-store.s3.amazonaws.com/0/72985/9dc76637-c5ad-d4f6-d35d-6db3a67707b2.png)

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

### **2011年 RFC6265**

![cookie-rfc6265.png](https://qiita-image-store.s3.amazonaws.com/0/72985/bd2cf879-2c5f-6c0e-f2f5-3cc360c45a16.png)

+ 前の仕様から約11年後の、2011年4月に[RFC6265](https://www.ietf.org/rfc/rfc6265.txt)として公開。前の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` ヘッダを記述することで各属性の設定が可能です。

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

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

![http-setcookie.png](https://qiita-image-store.s3.amazonaws.com/0/72985/a8ac5e22-bb29-a99c-ac68-6a0b0caf0429.png)

### **expires属性とMax-Age属性の違いは？**

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

+ [Internet Explorer 11 treats cookies unlike other modern browsers | Sundeep Malladi](https://balanceiskey.github.io/2015/11/25/ie11-cookies.html)

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

## **Cookieは何個も作成できるの？**

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

> Microsoft Internet Explorer は、次の RFC 2109推奨の最低限の制限に準拠しています。
>
> + 少なくとも 300 のクッキー
> + (Set-cookie ヘッダーの構文の説明ではターミナル以外の cookie を構成する文字のサイズを測定する) ように cookie あたり 4096 バイト以上
> + 一意なホスト名またはドメイン名ごとに少なくとも 20 の cookie

引用元：[Internet Explorer 内の cookie の数とサイズの制限](https://support.microsoft.com/ja-jp/kb/306070)

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

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

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

引用元：[Internet Explorer で、ドメインあたりの Cookie の数の制限が 20 から 50 に増加する](https://support.microsoft.com/ja-jp/kb/941495)

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

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

引用元：[RFC6265 — HTTP State Management Mechanism （クッキー処理仕様）日本語訳](https://triple-underscore.github.io/RFC6265-ja.html#section-6)

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

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

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

+ [ドメインあたりのクッキー数上限と上限に達した場合の挙動](http://please-sleep.cou929.nu/study-of-cookie-restrictions.html)
+ [2013年版 最新ブラウザの最大クッキーサイズ、数制限](http://d.hatena.ne.jp/hosikiti/20130925/1380098776)

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

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

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

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

![cookie-local.png](https://qiita-image-store.s3.amazonaws.com/0/72985/4c7a7ae3-c7b7-022f-518f-025f20b2ded2.png)

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

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

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

`Set-Cookie` ヘッダを定義するには、[setcookie](http://php.net/manual/ja/function.setcookie.php) 関数を使います。
`setcookie` 関数は、第7引数まであり、各設定が行えます。
各設定というのは、先ほど確認した `Set-Cookie` ヘッダの各属性のことです。

```php:PHP（setcookieの構文）
setcookie(Cookie名, Cookie値, 有効日時, パス, ドメイン, HTTPS接続のみ, Javascript無効）
```

```php:PHP（setcookieの記述例）
setcookie('a', 'b', time()+60*60, '/', 'xxxxx.com', true, true）
```

![php-setcookie.png](https://qiita-image-store.s3.amazonaws.com/0/72985/05188883-03b3-ba8d-b13d-e84e44e085aa.png)

引用元：[PHP: setcookie - Manual](http://php.net/manual/ja/function.setcookie.php)

引数に設定する項目は、先ほどの `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:PHP（Cookie削除の記述例）
setcookie('a', 'b', time()-1, '/', 'xxxxx.com', true, true）
```

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

## **PHPでCookieを取得する方法**

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

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

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

```txt:リクエストメッセージ（Cookieヘッダ）
Cookie: name=sato
```

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

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

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

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

```php:PHP
$name = $_COOKIE['name'];
echo $name;
```

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

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

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

+ [Qiita - $_GET, $_POSTなどを受け取る際の処理](http://qiita.com/mpyw/items/2f9955db1c02eeef43ea)

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

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

## **Cookieという名前の由来は？**

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

では、なぜクライアントに保存されるファイルまたはデータのことをCookieと呼ぶようになったのでしょうか。
Cookieを考案したNetscapeでブラウザを開発していた[ルー・モントゥリ](https://ja.wikipedia.org/wiki/%E3%83%AB%E3%83%BC%E3%83%BB%E3%83%A2%E3%83%B3%E3%83%88%E3%82%A5%E3%83%AA)氏が[ブログ](http://www.montulli-blog.com/2013/05/the-reasoning-behind-web-cookies.html)でCookieの由来について言及しているのが有力な説のようです。

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

> 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を勝手に操作！？「不正アクセス禁止法」で犯罪者にならないために](http://www.motex.co.jp/nomore/casestudy/3961/)

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

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

##**セッションって何？**

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

Wikipediaの [セッション](https://ja.wikipedia.org/wiki/%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3) を見るとわかりますが、下記の通り様々なところでセッションという用語が使われています。
理解できない用語も多々あるかと思いますが、セッションというのがどのような意味で使われているか確認してください。

> + セッション (コンピュータ)（英語版） - コンピュータの用語で、一連のインタラクティブな操作のこと。典型的にはログイン（ログイン）してからログアウト（ログオフ）するまでが一つのログインセッション（英語版）。
> + コンピュータネットワーク用語で、一連の通信のこと。典型的には接続を確立してから切断するまでが一つのセッション。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: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
<?php
session_start();
?>
<html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
</body>
</html>
```

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

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

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

### **初回のアクセス**

![session-first.png](https://qiita-image-store.s3.amazonaws.com/0/72985/b2a3b7f6-3cee-0264-2a08-22e048174833.png)

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

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

### **2回目のアクセス**

![session-second.png](https://qiita-image-store.s3.amazonaws.com/0/72985/d6cfb5eb-5e64-61ec-7aa8-2e27ef3cb12e.png)


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

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

### **3回目のアクセス**

![session-third.png](https://qiita-image-store.s3.amazonaws.com/0/72985/761d358d-5557-4371-40d8-4160ad6a82b8.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](http://www.php.net/manual/ja/session.configuration.php#ini.session.hash-function) を設定することで、種類の変更が可能です。
PHPのバージョンによって設定できる種類が異なります。
どのような種類があるのかなど、各自で調べてください。
ここでは、複雑な値を作成するのにいくつか種類があるということ、決済など絡む場合は、この種類について考慮する必要があるということを覚えておいてください。

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

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

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

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

![session-local.png](https://qiita-image-store.s3.amazonaws.com/0/72985/42d6ce88-8062-fc9a-790e-34e8b0b8fff4.png)

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

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

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

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

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

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

```php:PHP
$_SESSION['名前'] = 値
```

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

```php:PHP
$_SESSION['name'] = '佐藤';
```

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

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

```php: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: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_divisor`と`session.gc_probability`の値もphp.iniまたは`ini_set`関数で変更することが可能です。

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

+ [Qiita -［PHP］リクエストパラメータ・セッションに関するまとめ（有効期限ってどうなってるの？）](http://qiita.com/mpyw/items/7852213f478e8c5a2802#%E6%9C%89%E5%8A%B9%E6%9C%9F%E9%99%90%E3%81%A3%E3%81%A6%E3%81%A9%E3%81%86%E3%81%AA%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%AE)

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

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

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

```php:PHP
unset($_SESSION['name']);
```

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

```php: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 さんの

+ [Qiita -［PHP］リクエストパラメータ・セッションに関するまとめ](http://qiita.com/mpyw/items/7852213f478e8c5a2802)

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

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

+ [セッションハイジャック](http://securityblog.jp/words/87.html)（セッションIDを盗んでなりすまし）
+ [セッションフィクセーション](http://securityblog.jp/words/468.html)（セッションIDを強制する）
+ [クロスサイトリクエストフォージェリ](http://securityblog.jp/words/256.html)（操作の強要）

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

+ [SBクリエイティブ - 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版](https://amzn.to/2zaqyHf) 

が参考になると思います。
HTTP、Cookie、セッション、PHPについて基本的なことを理解していれば、何が原因でどのような対策すればよいのか理解できるかと思います。
偉そうなことを言ってますが、私もセキュリティについての理解が乏しいです（´ω｀；）
これからも研鑽したいと思います。

PHP関連の記事をまとめていますので、気になるものがあればご覧ください。
超入門というのは徹底入門ではなく、初学者向けに一つ一つ冗長に説明している超入門記事という意味です。

+ [Qiita - 【PHP超入門】式・文・構文・言語構造・制御構造について](http://qiita.com/7968/items/d739e926189e219273bd)
+ [Qiita - 【PHP超入門】値の代入と値渡しについて](http://qiita.com/7968/items/58015344ddab5d5a8b93)
+ [Qiita - 【PHP超入門】参照（リファレンス）の代入について](http://qiita.com/7968/items/d68a7aaebdc1711f4345)
+ [Qiita - 【PHP超入門】HTTP（GET・POST）について](http://qiita.com/7968/items/4bf4d6f28284146c288f)
+ [Qiita - 【PHP超入門】Cookieとセッションについて](http://qiita.com/7968/items/ce03feb17c8eaa6e4672)
+ [Qiita - 【PHP超入門】クラス～例外処理～PDOの基礎](http://qiita.com/7968/items/6f089fec8dde676abb5b)
+ [Qiita - 【PHP超入門】名前空間（namespace・use）について](http://qiita.com/7968/items/1e5c61128fa495358c1f)

今回の記事で超初心者の理解が少しでも深まれば幸いです。
最後まで読んでいただき、ありがとうございました。

# note

note でも記事を公開してるので、興味がある方はご覧ください。

・[【初学者向けコードリーディング】 PHP の TODO アプリのコードを一緒に読み解こう](https://note.mu/7968/n/n46a8beb87c93)