Programming log - Shindo200

イベント参加記録とプログラミング系の雑記

Railsのセッション管理方法について

セッション管理はWebアプリケーションを開発・運用するときに必ず関わってきますので、ある程度知っておかなければいけないことかと思うのですが、Railsのセッション管理について解説している資料が少ないように思えました。私もRailsのセッション管理についてあまり知識がありませんでしたので、簡単にですがまとめてみました。

なお、今回のエントリはセッション管理の大雑把な仕組みについて知っていることを前提にして進めていきます。セッション管理の仕組みについてご存知ない方は、『体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践』という書籍での説明がわかりやすかったので、そちらをご一読されると良いかと思います。

Railsのセッション管理方法

Railsでは、config/initializer/session_store.rbファイルにセッションの管理方法を指定します。デフォルトの設定ではCookieStoreというセッション管理を利用します。今回は「CookieStore」「ActiveRecordSessionStore」「DalliStore」の3つのセッション管理についてまとめていきます。

CookieStore

デフォルトの設定では、セッション管理に「CookieStore」という仕組みを使います。(正確には「EncryptedCookieStore」と呼びますが、ここでは「CookieStore」と呼ぶことにします。)CookieStoreは「セッションの中身を全てCookieに保存する」という仕組みを持つセッション管理です。試しに、セッションに値を格納する処理を行うアクションを用意して、このアクションにリクエストを送ってみます。

./app/controller/comments_controller.rb

class CommentsController < ApplicationController
  def index
    session['foo'] = 'foo'
  end
end

console

$ telnet localhost 3000
GET /comments HTTP/1.1
Host: localhost

/* レスポンスの一部 */
HTTP/1.1 200 OK
Set-Cookie: _application_session=aC9zaGlvSVppaXArSDV3SXI4N2tTbFFYZ3VHMVJMZU1PY2loTlBHd3ZiUnNYZnFlRDZsVE9UVWVvcEFBK2xXWTFseW91NTRzUlNWbjgzVnh1QkpucUR3N1IvaXNhZEM4VnpxbHFsQVlRQ2VMMVRyNy9OUHFIK3hIRDlrVlcrbHpENEdPVXREYWJKeE9lUmtLdHpQSEZrVnJJaU5PWENhRHF2R3JzcTFQQ0hGU2pVT1VmZUhydWpHb08xcXljQ2J0LS11SUpKaDJwd2ZFRjA2cnR0YkdHQkJ3PT0%3D--9016a8927b5818f7b06b9144bb4c5e80c91d7320; path=/; HttpOnly

レスポンスとして_application_sessionというキーを持つCookieが返ってきました。この_application_sessionというCookieの値の中に、セッションの内容が含まれているようです。

Cookieの値からセッションの内容を確認してみたいところですが、Rails4.0以降では内容が暗号化されていますので、Cookieの値からセッションの内容を簡単に確認することはできないようです。ただし、暗号化に使われた秘密鍵の値を知っているならば、複合化して、セッションの内容を確認することができるそうです。また、Rails3.2までは平文のままCookieに保存されていましたので、比較的簡単にセッションの内容を確認できていたそうです。

CookieStoreを利用するメリットとしてはRailsに付属しているライブラリだけで動かすことができること」「データベースなどへのアクセスが不要なので、処理が比較的早いこと」が上げられます。ただし、クライアントがセッションの内容を持つことになりますので、サーバーが好きなタイミングでセッションの内容を確実に消すことができないという問題があります。「悪意あるユーザーにCookieを盗まれてしまったときに、好きなタイミングでセッションの内容を消すことができないと、アカウントを長期間乗っ取られてしまう可能性があるのではないか」という話題があるようです。また、ユーザーはセッションの内容をメモしておけば、セッションの内容を好きなタイミングでメモしたときの状態まで戻すことができます。この仕組みを利用したサーバーの攻撃方法として「セッション再生攻撃」というものがあります。詳しいことは『Ruby on Rails Security Guide』で解説されていますので、是非ご一読ください。

CookieStoreはRailsデフォルトのセッション管理ですので、初心者はついつい利用してしまいがちです。しかし、クライアント側にセッション内容を保存するという仕様上、様々な問題を抱えていますので、利用しないほうがいいかと思います。

CookieStoreのまとめ

  • Railsデフォルトのセッション管理
  • Cookieにセッションの内容を保存する
  • セッションの内容は暗号化されているので、一般ユーザーが内容を簡単に確認することはできない(Rails4.0以降)
  • Rails標準の構成ですぐに使えること」と「処理が比較的早いこと」がメリット
  • クライアント側にセッション内容を保存するという仕様上、様々な問題を抱えている
  • Railsデフォルトだけど非推奨

ActiveRecordSessionStore

「ActiveRecordSessionStore」ActiveRecordを利用してDBにセッションの内容を保存するセッション管理です。利用するには、activerecord-session_store」というGemが必要です。

https://github.com/rails/activerecord-session_store

Readmeの内容に沿って導入すると、下記の構成のテーブルが作られます。

# == Schema Information
#
# Table name: sessions
#
#  id             :integer          not null, primary key
#  session_id     :string(255)    not null, 
#  data           :text
#  created_at     :datetime
#  updated_at     :datetime
#


Railsサーバを起動して、リクエストを送ってみます。

$ telnet localhost 3000
GET /comments HTTP/1.1
Host: localhost

/* レスポンスの一部 */
HTTP/1.1 200 OK
Set-Cookie: Set-Cookie: _session_id=b36a281518592c8811509f34f515a7f0; path=/; HttpOnly

レスポンスとして_session_idというキーを持つCookieが返ってきました。続けて、データベースのsessionsテーブルの中身を見てみます。

$ sqlite3 db/development.sqlite3
sqlite> .mode line
sqlite> SELECT session_id, data FROM sessions;
session_id = b36a281518592c8811509f34f515a7f0
      data = BAh7B0kiCGZvbwY6BkVGSSIIZm9vBjsAVEkiEF9jc3JmX3Rva2VuBjsARkki
MVhKblEvbkxrdXlKeHBwWTZSYU96UXVjQjY1UVBMMDJSMkJOeVR1cmdmSUU9
BjsARg==

レスポンスに含まれていた_session_idと同じ値を持つレコードがsessionsテーブルに追加されています。このレコードのdataカラムの中に、セッションの内容が保存されているようです。とてもシンプルなセッション管理ですね。

ActiveRecordSessionStoreはGemを1つ追加するだけですぐに利用できますので、比較的簡単に導入できます。また、サーバーがセッションの内容は持つことになるので、CookieStoreが抱えていた様々な問題を解決できます。処理速度が気になるところですが、簡単に扱えるセッション管理をお探しでしたら、ActiveRecordSessionStoreという選択もあるのではないでしょうか。

ActiveRecordSessionStoreのまとめ

  • ActiveRecordを利用してDBにセッション内容を保存するセッション管理
  • activerecord-session_store」というGemが必要
  • 導入が比較的簡単
  • サーバー側にセッション内容を保存するので、CookieStoreが抱えていた様々な問題を解決できる

DalliStore

「DalliStore」memcachedにセッションの内容を保存するセッション管理です。「Dalli」というRubyのmemchacedクライアントGemに付属しています。RubyのmemchacedクライアントはDalli以外にもいくつか存在するのですが、HerokuのチュートリアルにはDalliを使った導入例が書いてありましたので、このエントリでもDalliを使うことにします。

mperham/dalli · GitHub

導入方法についてはReadmeをご一読ください。導入できましたら、memcachedを起動して、Railsサーバーにアクセスしてみます。

# memcachedをデーモンモードで起動
$ memcached -d
$ telnet localhost 3000
GET /comments HTTP/1.1
Host: localhost

/* レスポンスの一部 */
HTTP/1.1 200 OK
Set-Cookie: _session_id=6599177fbe4c96725e90a3bf0ce6a4c7; path=/; expires=Sun, 02 Nov 2014 06:00:44 -0000; HttpOnly

ActiveRecordSessionStoreと同じように、_session_idというキーを持つCookieが返ってきました。(Set-Cookieにexpiresオプションが付いていますが、これはDalliのReadmeに記載されている導入方法に合わせて、期限付きのセッションIDを返すように設定を書いたために付きました。)続けて、memcachedの中身を見てみます。memcachedはデフォルトでは11211ポートで起動しますので、11211ポートに接続すれば、memcachedと対話することができます。

$ telnet localhost 11211

memcachedに接続できましたら、下記のコマンドでセッションIDに対応するセッション内容を確認できます。

get _session_id:6599177fbe4c96725e90a3bf0ce6a4c7

「get _session_id:」の後の文字列にはセッションIDを入力します。このコマンドを実行すると、下記のような結果が返ってきます。

VALUE _session_id:6599177fbe4c96725e90a3bf0ce6a4c7 1 94
{Ifoo:EFIfoo;TI"_csrf_token;FI"1v7q3nI5BJznOdoll00iroIlAkPhvnhS7mwIotU2bOlQ=;F
END

memcachedのドキュメントによると、2行目の部分が値を表しているそうです。今回はセッションのfooというキーに"foo"という文字列を保存してみましたが、それらしき値が見えていますね。確認できましたら、quitコマンドで接続を切りましょう。

DalliStoreはActiveRecordStoreSessionと比べて導入が少し複雑です。また、memcachedの扱いに慣れていない方は、問題が起きたときの対応に苦労すると思います。しかし、処理速度が早いですし、memcached自体が様々な機能を持っていますので、使いこなすことができれば便利なのかもしれません。

DalliStoreのまとめ

  • memcachedにセッション内容を保存するセッション管理
  • memcachedとDalliが必要
  • サーバー側にセッション内容を保存するので、CookieStoreが抱えていた様々な問題を解決できる
  • ActiveRecordSessionStoreと比べて、導入が大変だが、処理速度が早い
  • 初心者が扱うのは難しいが、使いこなせれば便利なのかも?