84
79

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2012-12-26

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 のみを持たせる一般的な実装になる。

84
79
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
84
79

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?