【40日目】Webの基礎知識まとめ２　Cookieとセッションについて

今日も引き続き[「プロになるためのWeb技術入門」](https://www.amazon.co.jp/%E3%80%8C%E3%83%97%E3%83%AD%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AEWeb%E6%8A%80%E8%A1%93%E5%85%A5%E9%96%80%E3%80%8D-%E2%80%95%E2%80%95%E3%81%AA%E3%81%9C%E3%80%81%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AFWeb%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E9%96%8B%E7%99%BA%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%AE%E3%81%8B-%E5%B0%8F%E6%A3%AE-%E8%A3%95%E4%BB%8B/dp/4774142352)で学習を進めました。
その中で、ずーっと言葉は聞いたことがあったけれど、なんのことかよく分かっていなかった２つの仕組みについてまとめました。

「Cookie」と「セッション」です。

## はじめに　〜HTTPの問題点
#### ステートレスなプロトコル　HTTP
HTTPプロトコルはもともとWEBアプリケーションのために設計された通信プロトコルではありませんでした。
HTTPよりも以前から使われていた通信プロトコルであるFTPの欠点を補うような形で設計されたものだったのです。

FTPはもともとコンピュータ間でファイルを転送するために作成された「ステートフル（状態をもつ）・プロトコル」と呼ばれるものでした。これは、リクエストを実行したい場合には、必ずサーバーにユーザ名とパスワードを伝える必要があり、それによって終了するまでの間、サーバが各リクエストを覚えていて、それを踏まえたリクエストを実行することができるようなものです。
この方法ではユーザーごとにその状態を保存しておかなければならないので、非常に手間がかかること（オーバーヘッド）と、毎回認証が必要となることが問題でした。

そのため、認証が不要で、履歴を保存する必要がなく、実装も簡単な通信プロトコルが必要とされており、それを実現したのがHTTPなのです。

HTTPでは認証することもなく、例えばGETリクエストを送れば、希望のファイルをレスポンスとして得ることが可能です。
一方で、ステートレスな通信プロトコルにおいては、リクエストに対してレスポンスを返すことしかできず、１つのHTTPリクエストにつき１つのファイルしかダウンロードできません。


## Cookie
#### Cookieとは
もともとHTTPもFTP同様Webアプリケーションでの使用を想定せずに作られているので、当時は簡単にファイルが転送できればよかったのです。
しかし例えば認証を必要とするようなWebアプリケーションをHTTPプロトコルで実現するには、なんとかしてステートレスな方法で状態を表現する必要があります。
GETリクエストでURLに認証情報を入れるわけにもいかないし、POSTメッセージで毎回認証情報を渡そうとすると、全てのページでログインフォームに入力し続けなければログイン状態を維持できません。
それを解決する方法が、Cookieです。

#### Cookieの仕組み
Webブラウザで認証フォームに入力したログイン情報は、POSTリクエストによってサーバーに送信されます。
その初回の認証の際に、WebサーバからWebブラウザへ、レスポンス・ヘッダ内で「Cookie」というヘッダにそれらのログイン情報が「名前＝値」の形で格納されて渡されます。

```
#レスポンス・ヘッダ 
Set-Cookie: user=Taro
Set-Cookie: pass=password
```
これがCookieの正体です。

初回ログイン時のSet-Cookieは、例えばPHPであれば「setcookie」という関数によって実現しています。
※教材は2010年のものですので、バージョン更新のためコードが古い可能性があります。ここでは記法ではなくWebの仕組みの理解を目的としているので、細かく言及しません。

```PHP:do_login.php
<?php
  //フォーム入力された内容を取得する
  $user = $_POST['user'];
  $pass = $_POST['psss'];

  //ユーザ名とパスワードを確認する
  if(strcmp($user, 'Taro') == 0 && strcmp($pass, 'password') == 0) {
   //ユーザ名とパスワードが正しい場合
   setcookie("user", $user);
   setcookie("pass", $pass);
   header('location: item_list.php');
 } else {
   //ユーザ名とパスワードが間違っている場合
   header('Location: login_failed.html');
 }
?>
```

初回ログイン時にCookieを受け取ったWebブラウザは、次回以降同じWebサーバにアクセスする際、受け取ったCookieの情報をそのままリクエスト・ヘッダに格納して渡します。
渡されたCookieはクライアントPC上に保存されます。

```
# リクエスト・ヘッダ 
Cookie: user=Taro; pass=password
```
Webアプリケーション側ではリクエスト・ヘッダのCookieを調べることでアクセスしてきた相手が誰かを知ることができます。このチェックは下記のようなコードで実装しています。

```php:example_list.php
<?php
  //キャッシュを無効にする
  header('Cache-Control: no-cache');

  // Cookieの内容を取得する
  $user = $_COOKIE['user'];
  $pass = $_COOKIE['pass'];

  //ユーザ名とパスワードを確認する
  if(strcmp($user, 'Taro') != 0 || strcmp($pass, 'password') != 0) {
   //ユーザ名とパスワードが誤っている場合 => Cookieを削除
   setcookie("user", "", time() - 3600);
   setcookie("pass", "", time() - 3600);
   header('Location: login_failed.html');
   exit();
 }
?>
```
正しい場合には、この後に正しい処理を記述しておけばそれが実行されます。
間違っている場合にはCookieを取り消してログインページにリダイレクトされます。

ログアウトボタンを押した場合にも、同様にCookiを取り消してログインページにリダイレクトします。

## セッション
#### セッションとは
セッションとは、接続を確立してから切断するまでの一連の通信のことです。
Webアプリケーションにおいては、ログインからログアウトまでの間にやりとりされる、HTTPリクエストとHTTPレスポンスの繰り返しによって行われることになります。

例えば、ログイン -> 商品の選択 -> 注文内容確認 -> ログアウト というような流れです。
もちろん、この間には複数回のHTTP通信のやりとりが必要になるので、状態を保存しておく必要がありますので、先述のCookieという技術が役に立ちそうです。


#### 安全に状態を保存するための技術　セッション
Cookieで仮に状態を保存しようとすると、Cookieにはユーザ名やパスワードを格納することにしますが、CookieはHTTPのヘッダに行われているので、見ようと思えば簡単にのぞくことができます。
また、Cookieとして重要な認証情報を送ってしまうと、クライアントPC上に保存されるため、比較的脆弱です。
他にもユーザ名やパスワードといったログイン情報は必ずしもユニークではないため、誤って他のユーザの認証を通してしまう可能性もあります。

こういった重要な情報を直接Cookieに保存しないようにしたり、状態を保存する際にユニークな識別情報と結びつけるために、セッションで管理する方法がとられます。

####セッションの仕組み
まずWebアプリケーション側でセッションの進行状況を管理します。実際にはメモリや、データベースを使用します。
そしてWebクライアントごとにユニークなID（セッションID）を発行し、CookieにはこのセッションIDのみを格納します
例えばPHPでは「PHPSESSID」というパラメータをCookieに格納します。

CookieにてクライアントからセッションIDを受け取ったWebサーバは、データベースなどを参照することで状態を保存します。

## まとめ
最終的にCookieを使ったセッションの管理は下記のような流れで行われることになります。

初回ログイン時
1　ユーザ名、パスワードなどで認証チェックを行う。
2　セッションIDを発行し、セッション情報にログイン中ユーザ名を記録
3　CookieにセッションIDを格納してWebクライアントに渡す。

2回目以降
１　セッションIDを格納したCookieをWebサーバに渡す。
２　メモリやデータベースを参照し、受け取ったCookie内のセッションIDからログインしてきたユーザ名を識別する。