80
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Rails で (Rack の) セッション情報を Cookie に保存する仕組み

Rails では特に (config/initializers/session_store.rb などで) 設定しない場合、セッションの情報をクライアントの Cookie に直接、(事実上) 平文で保存する。

実際に見てみよう。

$ rails new todo
$ cd todo
$ rails g scaffold tasks name
$ rake db:migrate
$ rails s
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter do
    # アクセスした日時を session に保存
    session[:last_accessed_at] = Time.now
  end
end
$ curl localhost:3000/tasks -i -s | grep Set-Cookie
Set-Cookie: _todo_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTE0YTYyZmNlYmVhODVmNjU5NGZmNjlhN2M0MTM1ZjQ0BjsAVEkiFWxhc3RfYWNjZXNzZWRfYXQGOwBGSXU6CVRpbWUNREscgLb0JXIHOgtAX3pvbmVJIghKU1QGOwBUOgtvZmZzZXRpApB%2BSSIQX2NzcmZfdG9rZW4GOwBGSSIxU1llVWZyclBzVU9oUG0rclRMQ1hkUzNpYUU1RlpRWGI0QUU2alJ5UDJEYz0GOwBG--b7446faf0dcddf99f358ce40525f7eade939d1be; path=/; HttpOnly
require "uri"
require "base64"
require "pp"

# 上で Set-Cookie ヘッダに書かれてる文字列
session_in_cookie = "BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTYxMzE4MDQ5ZDA2ZjM1NjlhNGJmM2I2ZDMwYjJmMDA4BjsAVEkiFWxhc3RfYWNjZXNzZWRfYXQGOwBGSXU6CVRpbWUNREscgJmmKHsHOgtAX3pvbmVJIghKU1QGOwBUOgtvZmZzZXRpApB%2BSSIQX2NzcmZfdG9rZW4GOwBGSSIxOHcrUGlocmJIRDhpR01QZVRVMkVzZ0xsOVVJQnk4eEU0bzlpa3RoYVNvbz0GOwBG--394e1c8682803d5109d2bfbffe4254430e41098e"

# URI デコード、-- の前を取り出し
session_base64, digest = URI.decode(session_in_cookie).split("--")

# Base64 デコードして Marshal.load でオブジェクト復元
pp Marshal.load(Base64.decode64(session_base64))
# {"session_id"=>"61318049d06f3569a4bf3b6d30b2f008",
#  "last_accessed_at"=>2013-03-26 13:30:50 +0900,
#  "_csrf_token"=>"8w+PihrbHD8iGMPeTU2EsgLl9UIBy8xE4o9ikthaSoo="}

このように Base64 のデコードと Marshal.load で簡単に復元できてしまう。これだといっけんユーザがセッションの内容書き換え放題にみえるが、実際にはそんなことはない。

上のコードでさらっと split しているが、Cookie に格納される情報は Marshal.dump -> Base64 エンコードした文字列のあと -- に続いて、ハッシュ値が書かれている。これは -- より前の文字列と秘密鍵を使って OpenSSL::HMAC.hexdigest で作成したもので、Cookie 生成時に付与され、受け取ったときに一致するか検証される。

# rack-1.4.1/lib/session/cookie.rb より
module Rack
  module Session
    class Cookie
      def generate_hmac(data, secret)
        OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
      end
    end
  end
end

このため、セッションの中身を書き換えたところで、秘密鍵を知らない限り、適切なハッシュ値を付与することができず、改ざんを防げるわけだ。

この秘密鍵は Rails アプリケーション作成時にランダムに生成されるもので、下記のように設定されている。長い。

# config/initializers/secret_token.rb
Todo::Application.config.secret_token = 'aeb84aaf07fb5b4f8ebbfcbd8e524b64e595a436fffe0b64a54d4367c99c02bf9bef939755af13a49117fa3e526502fc297948f936e33984b9503afbeb1b359c'

素人目には充分なように安全にみえる (詳しい方のつっこみ歓迎)。

なお、最初に見たように session の内容は、リクエストした人本人には丸見えなので、秘密にしておくべき情報をセットしないよう注意。

また、CookieStore 以外の方式、たとえば DB にセッションの情報を記録する場合では、Cookie には session_id のみを持たせる一般的な実装になる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
80
Help us understand the problem. What are the problem?