背景
- Padrinoでログイン情報などを保持するのにsessionを使っています。
- 通常は
config/apps.rb
かapp/app.rb
でenable :sessions
と記述してsessionを有効にしますが、この方法だとsessionの内容は全てcookieに入ってしまいます。 その気になれば簡単にMarshal.load
で内容を見ることができます。 - cookieにはsession idのみ格納し、内容はサーバサイドに保存する方が安全だろうし、通信量も少し減るかな、と思って方法を探りました。
試したもの
- 少し昔の記事ですが、これを参考にしてテストしてみました。
-
http://takaaki-kasai-tech.blogspot.com/2014/05/how-to-use-rack-session-pool-on-padrino.html
- これは
Rack::Session::Pool
を使う例。このmiddlewareは@pool
にsessionの内容を保存し、cookieにはsession idのみ格納します。 - 特にサーバサイドで設定が不要なのが手軽な感じです。
- アプリケーションを終了すると内容が消失します。
- これは
-
http://takaaki-kasai-tech.blogspot.com/2014/05/session-management-by-rack-session-redis.html
- これは
Rack::Session::Redis
を使う例。このmiddlewareは key-valueストアであるredisにsessionの内容を保存し、cookieにはsession idのみ格納します。 - サーバサイドでredisを動かす必要があります。開発機でも動かす必要があります。
- アプリケーションを終了しても内容が残ります。
- これは
-
http://takaaki-kasai-tech.blogspot.com/2014/05/how-to-use-rack-session-pool-on-padrino.html
コード
-
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-helpers
のform_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_csrf
がtrue
なら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
とすればいいのですが如何にも面倒臭いし、書き忘れてエラーを出しそうな感じです。
-
結局解決しなかったのですが、以下のような力技もあります。
スマートなやり方はないものでしょうか。
Padrino::IGNORE_CSRF_SETUP_WARNING=true
とset :protect_from_csrf, true
を併用するのがよいのかな?