LoginSignup
1
0

More than 5 years have passed since last update.

Padrinoのsessionをredisに保存しようとした

Posted at

背景

  • Padrinoでログイン情報などを保持するのにsessionを使っています。
  • 通常は config/apps.rbapp/app.rbenable :sessions と記述してsessionを有効にしますが、この方法だとsessionの内容は全てcookieに入ってしまいます。 その気になれば簡単に Marshal.load で内容を見ることができます。
  • cookieにはsession idのみ格納し、内容はサーバサイドに保存する方が安全だろうし、通信量も少し減るかな、と思って方法を探りました。

試したもの

  • 少し昔の記事ですが、これを参考にしてテストしてみました。

コード

  • app/app.rbに以下の記述を行ないました。

        set :protection, false
        set :protect_from_csrf, false
        disable :sessions
        use Rack::Session::Redis, path: '/', domain: nil, expire_after: 86400
        use Rack::Protection
        use Rack::Protection::AuthenticityToken
    

難点

  • どちらのmiddlewareを使っても、POSTメソッドでアクセスするとforbiddenと言われてしまいました。サーバでは

    attack prevented by Rack::Protection::AuthenticityToken
    

    というメッセージが出ます。

  • Rack::Protection::AuthenticityToken を使用すると、CSRFチェックが行なわれます。set :protect_from_csrf, true しているのと同様ですが、後者の場合はpadrinoのform helperが自動的にCSRFチェック用のトークンを送るinputタグを作ってくれます。

    <form action="/a" accept-charset="UTF-8" method="post">
    <input type="hidden" name="authenticity_token" value="何らかのトークン" />
    <input id='key' name='key' type='text'>
    ...
    
  • この動きは、padrino-helpersform_helpers.rb の中の Padrino::Helpers::FormHelpers#formtag で以下のように記述されています。

      def form_tag(url, options={}, &block)
        options = {
          :action => escape_link(url),
          :protect_from_csrf => is_protected_from_csrf?,
          'accept-charset' => 'UTF-8'
        }.update(options)
        options[:enctype] = 'multipart/form-data' if options.delete(:multipart)
    
        if (desired_method = options[:method]) =~ /get/i
          options.delete(:protect_from_csrf)
        else
          options[:method] = 'post'
        end
        inner_form_html = hidden_form_method_field(desired_method)
        inner_form_html << csrf_token_field if options.delete(:protect_from_csrf)
        concat_content content_tag(:form, inner_form_html << capture_html(&block), options)
      end
    
    • オプション protect_from_csrftrue なら csrf_token_field が 生成されます。デフォルト値として is_protected_from_csrf? の返り値が指定されていて、 このメソッドは settings.protected_from_csrf を見ています。従って set :protect_from_csrf, true しないとこれは true になりません。
    • ところが、これを行なうと、padrinoアプリケーション起動時に padrino-core/application/application_setup.rb
      sessionが有効かをチェックするので、

      you need to set up a session middleware *before* Rack::Protection::AuthenticityToken (RuntimeError)

    というエラーになります。

解決

  • 理論的には全てのフォームの作成時にpadrinoのヘルパで

    form_tag(url(:a, :index), method: :post, protect_from_csrf: true) do

とすればいいのですが如何にも面倒臭いし、書き忘れてエラーを出しそうな感じです。

  • 結局解決しなかったのですが、以下のような力技もあります。

    • こんなモンキーパッチを書いて、helperとして読み込ませます。

      module Padrino::Helpers::FormHelpers::Security
        def is_protected_from_csrf?
          MyProject::App.middleware.map(&:first).include?(Rack::Protection::AuthenticityToken)
        end
      end
      

      結論

  • スマートなやり方はないものでしょうか。

  • Padrino::IGNORE_CSRF_SETUP_WARNING=trueset :protect_from_csrf, true を併用するのがよいのかな?

1
0
0

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
1
0