railsでsession情報をredisに保存する方法(redis-serverの導入)


永続化可能なKVSとしてよく使わているredisについて説明します。主にdevelop環境での設定のみの記事なります。

redisは、key-valueの関係でデータを保存することができ、session管理や、sidekiqでのメールの永続化など様々なシーンで使われています。

セッションとクッキーの違い

セッションとクーキーが出て来るので違いを一言で。
セッション・・・複数リクエストを同一のユーザーによるものと認識すること
クッキー・・・セッションを実現するための手段

通常、http通信はステートレスと言われ、それぞれの通信は独立しているが、それだと例えばログインしているユーザーが通信を行うためたびに毎回確認が必要になってしまうので、クッキーという手段を使って、同じログインユーザーのリクエストを同一のユーザーによるものと認識するセッションを実現します。

セッション情報の保存場所をredisにする理由

  • サーバ複数台構成になってロードバランサで処理分散した場合にセッション情報を使い回せるように
  • デフォルトのクッキーストアはセキュリティ的に脆い →厳密のいうと、デフォルトのクッキーストアでも暗号化されてブラウザに保存されるのでなりすましなどのリスクが高いわけではないが。

redisのインストール

macでredisをインストールするためにはbrewを使用します。

$ brew install redis

Railsでsession storeにredisを使用する

gemの導入

railsでredisを使う際にはredis-railsがよく使われます。
Gemfileにredis-railsを記載し、bundle installをします。

gem 'redis-rails'

$ bundle install --path vendor/bundle

sessionの保存設定(ここがミソ)

railsはデフォルトでconfig/initializers/session_store.rbにセッションの設定が書いてあります。
まずこのデフォルトの設定をコメントアウトします。

その後、development環境に設定する場合は、

config/environments/develpoment.rb

config.session_store :redis_store, servers: 'redis://localhost:6379', expire_after: 1.day

に記載。

redisサーバーを起動

$ redis-server
75400:C 10 Feb 2020 00:20:51.337 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
75400:C 10 Feb 2020 00:20:51.338 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=75400, just started
75400:C 10 Feb 2020 00:20:51.338 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 75400
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

75400:M 10 Feb 2020 00:20:51.344 # Server initialized
75400:M 10 Feb 2020 00:20:51.345 * DB loaded from disk: 0.000 seconds
75400:M 10 Feb 2020 00:20:51.345 * Ready to accept connections


redis-serverを導入している場合、redis-serverを立ち上げずに、localhost:3000にアクセスすると、
→Connection refused - connect(2) for 127.0.0.1:6379
というエラーが出ますので、ご注意を!

redisの中身

redisの中をのぞいてみるとクッキーに保存されているsession_idがわかります。
session_idというkeyに対するバリューというハッシュ形式で保存・管理されています。

まず、$ redis-cliでredisに接続し、keys *でredisに保存されているキーの一覧が見られます。

$ redis-cli
127.0.0.1:6379> keys *
 1) "queues"
 2) "stat:processed:2020-01-30"
 3) "stat:processed:2020-01-25"
 4) "stat:failed"
 5) "2::bd503b1718ff6d40ebbea425f65118e00b74f5c7da051dd70e1d942662f9dbc5"
 6) "queue:mailers"
 7) "stat:processed"
 8) "stat:failed:2020-01-30"
 9) "2::c0928f6f585c2e9a4138aa1c1fe40679eaa33ca884c7fd4661ac49f8e9901892"
10) "stat:failed:2020-01-25"

例えば、(9)の"2::c0928f6f585c2e9a4138aa1c1fe40679eaa33ca884c7fd4661ac49f8e9901892"の部分が、確認できたキー(セッションID)ですね。

さらに、get 2::c0928f6f585c2e9a4138aa1c1fe40679eaa33ca884c7fd4661ac49f8e9901892というコマンドを打つとそのキー(セッションID)に紐づくバリュー(セッション情報など)を確認することができます。

127.0.0.1:6379> get 2::c0928f6f585c2e9a4138aa1c1fe40679eaa33ca884c7fd4661ac49f8e9901892
"\x04\b{\tI\"\x0fsession_id\x06:\x06ETo:\x1dRack::Session::SessionId\x06:\x0f@public_idI\"E8fa23d9c40882684d78f6c3ecdf6ac0475d1c67b16a5021c9bcac5fa56a1d677\x06;\x00FI\"\tcsrf\x06;\x00FI\"1qRYmNiN9C6xDHE4hXu2OVvM/Rer+W/WhmV3h/koEngc=\x06;\x00FI\"\rtracking\x06;\x00F{\x06I\"\x14HTTP_USER_AGENT\x06;\x00TI\"-2bc71067222d9672fb261f3a4ace789ebf9606d2\x06;\x00FI\"\x10_csrf_token\x06;\x00FI\"1TRhm5z6eTQnTewVT7Ro5fElJ7ROm9hPlixwOdjB/ApY=\x06;\x00F"

バイナリっぽくコンバートされちゃって見づらいんですが、よくみると_csrf_tokenとかが確認できますね。

  • Railsのデフォルトであるクッキーストアでの管理方式だと、この_csrf_tokenなどの保存場所がクッキーになります。
    クッキーのバリューを復号化することでと_csrf_tokenやuser_idなどが取得できます。

  • redisストアでの管理方式だと、この_csrf_tokenなどの保存場所がredisになります。キーとバリューのハッシュ形式で保存されています!

ブラウザの開発者ツールで簡単にクッキーを確認できる

ApplicationタブのCookiesという項目をみると現在ブラウザに保存されているCookiesの一覧が見られます。

redisを使わずRailsのデフォの設定だと、この画像でいうところの_runteq_normal_sessionというキーに対してSession情報を暗号化したものがバリューとして設定されます。

redisでの管理に変更すると_session_idというキーに対してセッションIDがバリューとして設定されるのが一目でわかります。

ただ、実際にブラウザに保存されているクッキー情報のvalueに入っているセッションIDと、redis側で保持しているセッションID(キー)は一致しませんが、ソースとしては同じものを指します。
ここの理解としては、redisが発行したセッションIDをクッキーにvalueとして入れてブラウザに渡すときに、セッション情報に紐づくランダムな数字でセッションIDを変換してcoockiesに渡しているので、一致していないと考えられます。

また、このセッションIDは設定により、指定した期間でredis側でリセットすることも可能なので、きちんと設定しておけば、こちらブラウザのクッキー情報を盗まれても、リセットしてしまえば、不正にアクセスすることはできない仕組みになっています。