# Cookieとセッション これまでは状態を扱わない、いわゆるステートレスなWebの技術について学んできた。今回はWeb上で(クライアントとサーバ間で)状態を扱う方法について学んでいく。 ## Cookie Cookieとは、**WebクライアントとWebサーバ間で状態を扱うためのプロトコルまたはそのために保存されたデータのこと**である。 HTTPリクエスト、レスポンスにはキー・バリュー形式で与えるHTTPヘッダーと呼ばれる領域があるが、Cookieはここでやり取りされる。 サーバからクライアントに向けて保存するCookieを指定するときは`Set-Cookie`を、クライアントのCookieをサーバに向けて伝えるときには`Cookie`をヘッダーに付与する。 ![](https://i.imgur.com/uY0ZgYH.png) ただ、HTTPヘッダーは簡単に読み取ることができてしまうため、読み取られると困る情報をやり取りする場合には対策が必要である。 ## セッション セッションとは**Web上で行われるアクションの一連の流れのこと**である。例えばショッピングサイトでは、商品をカートに入れ、住所等の情報を入力し、注文を確定するまでのアクションのまとまりがセッションである。 またセッションは他に、サーバがクライアントを識別できる期間のことも言う。ステートレスであればサーバは同じクライアントからリクエストが来たとしても、それが同じクライアントからのものであることを識別できないので、セッションはリクエストに対するレスポンスを返す間のみとなる。 ![](https://i.imgur.com/mGI9d8n.png) ショッピングサイトの例のように、複数のリクエストとレスポンスをまたぐセッションを扱う必要がある場合、同じクライアントとやり取りしていることを識別できなければいけないため、セッションを管理する仕組みが必要である。 通常はセッション管理にはCookieが利用される。サーバがセッションごとにIDを設定してクライアントと共有することで、リクエストに含まれるCookieからクライアントを識別することができるようになる。 ![](https://i.imgur.com/wONOAoa.png) CookieにIDだけでなくIDに関連づけられた他のデータも保存する方法がある。サーバにデータを保存する必要がなくなるので実装が楽になるが、データはクライアントに保存されているので簡単に改ざんすることができてしまう。また、リクエストやレスポンス時にデータのやり取りがされるため、盗聴される可能性が高くなる。そのため、書き換えられても問題のないデータのみにするか、改ざんを検出したり、盗聴の対策をする仕組みを考えなければならない。他にも、保存するデータの量によって通信が重くなる場合もあるので、必要のない限りはIDのみをCookieに保存する方が良い。 **チェックポイント** - Cookieはクライアントとサーバのどちらに保存されるか? - CookieはHTTPリクエスト・レスポンス中のどこを使ってやり取りされるか? - ショッピングサイトの例でセッションを利用しなかった場合、各アクションでする必要のあることは何か? ## Sinatraでのセッション管理 以下のプログラムはSinatraでセッション管理を利用した例である。 ```ruby require 'sinatra' class MainApp < Sinatra::Base use Rack::Session::Pool, expire_after: 2_592_000 get '/start_session' do session[:data] = params[:data] session[:data_length] = params[:data].length "Session ID: #{session.id}\nData: #{session[:data]}\nLength: #{session[:data_length]}\n" end get '/destroy_session' do destroied_session_id = session.id session.destroy "Destroied session ID: #{destroied_session_id}\n" end get '/check_session' do if session[:data] then "Session ID: #{session.id}\nData: #{session[:data]}\nLength: #{session[:data_length]}\n" else "The session is empty.\n" end end end ``` https://github.com/SCCP2016/sinatra-session/tree/master `enable :sessions`でセッション管理を有効にして、`session`でCookieの保存と参照を行なっている。 `session`オブジェクトを使ってCookieで保存するデータにハッシュのような形でアクセスすることができる。上のプログラム中の`session[:data]`はただセッションIDに紐づけられた`:data`という名前のデータにアクセスしているだけなので、名前は`:data`でなくても良い。 この例ではセッションIDが盗聴された場合、なりすましが可能である。そのため、セキュアにセッション管理をするためにはHTTPSを使うなどの対策が必要である。 - GET /start_session?data=[string] セッションのデータ管理を開始する。 - GET /destroy_session セッションのデータを削除して、新しいセッションを作成する。 - GET /check_session セッションのデータ管理が開始されていれば、そのデータを出力する。 開始されていなければ、データが空であることを表示する。 **動作確認** curlで動作確認するときは以下のオプションを利用する。 - `-c FILE` ... FILEにSet-Cookieの内容を保存 - `-b FILE` ... FILEの内容をCookieに読み出し 実行例 ``` $ curl -c cookie_file http://www.abcd.efg/hij $ curl -b cookie_file http://www.abcd.efg/hij $ curl -b cookie_file -c cookie_file http://www.abcd.efg/hij ``` **チェックポイント** - セッション管理を有効にしたとき、`session[:cart]`は何を参照しているか? <!-- **演習問題** sinatra-sessionのテストが全て通るようにapp.rbを書き換えなさい。