Note

3年後の自分のために書いています

Rails の API モードでセッションを有効にする

巷には config/application.rb 内で Rails.app_class.config.api_only = false にすればできる、的な記事が溢れているが、不要な middleware まで読み込みたくない。

環境

差分

デフォルトの CookieStore を使う場合

diff --git a/config/application.rb b/config/application.rb
index 773bbeb..19e3c5b 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -35,5 +35,11 @@ module RailsCsrfProtectionApi
     # Middleware like session, flash, cookies can be added back manually.
     # Skip views, helpers and assets when generating a new resource.
     config.api_only = true
+
+    # Added middleware manually.
+    config.middleware.use ActionDispatch::Cookies
+    config.middleware.use ActionDispatch::Session::CookieStore
+    config.middleware.use ActionDispatch::ContentSecurityPolicy::Middleware
   end
 end

config.middleware.use ActionDispatch::ContentSecurityPolicy::Middleware が必要というのが分からずハマった。

👆 しないでも _session_id は保存されたが、session[:user_id] などの情報が保持されなかった。(再度リクエストした時に取得できなかった)

Rails と Rack - Railsガイド

  • ActionDispatch::ContentSecurityPolicy::Middleware

Content-Security-Policyヘッダー設定用のDSLを提供します。

とのこと。これを使うことで _session_id が暗号化(?) されていた。

api_only = false にすると {app_name}_session というキーに暗号化された値が入る(_session_id にも入るけど)

CacheStore を使う場合 ~memory_store の場合~

diff --git a/config/application.rb b/config/application.rb
index 773bbeb..19e3c5b 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -35,5 +35,11 @@ module RailsCsrfProtectionApi
     # Middleware like session, flash, cookies can be added back manually.
     # Skip views, helpers and assets when generating a new resource.
     config.api_only = true
+
+    # Added middleware manually.
+    config.middleware.use ActionDispatch::Cookies
+    config.middleware.use ActionDispatch::Session::CacheStore
+    config.middleware.use ActionDispatch::ContentSecurityPolicy::Middleware
   end
 end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index dc78c00..cbbc46a 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -14,18 +14,11 @@ Rails.application.configure do
   # Show full error reports.
   config.consider_all_requests_local = true

-  # Enable/disable caching. By default caching is disabled.
-  # Run rails dev:cache to toggle caching.
-  if Rails.root.join('tmp', 'caching-dev.txt').exist?
-    config.cache_store = :memory_store
-    config.public_file_server.headers = {
-      'Cache-Control' => "public, max-age=#{2.days.to_i}"
-    }
-  else
-    config.action_controller.perform_caching = false
-
-    config.cache_store = :null_store
-  end
+  # Enable caching.
+  config.cache_store = :memory_store
+  config.public_file_server.headers = {
+    'Cache-Control' => "public, max-age=#{2.hour.to_i}"
+  }

   # Print deprecation notices to the Rails logger.
   config.active_support.deprecation = :log

Rails.app_class.config.cache_store に何も指定しなければ、デフォルトの :file_store になり、 path には "#{root}/tmp/cache/" が使われる。(Rails.app_class.config.cache_store = :file_store と書いた場合は明示的に path の指定が必要になる)

api_only の設定による middleware の差分

Rails.app_class.config.api_only が true の場合と false の場合の差分。

$ rails middleware で確認できる。

false の方がたくさん middleware が使われている。

--- tmp/api_true.txt 2020-05-05 05:16:23.000000000 +0900
+++ tmp/api_false.txt 2020-05-05 05:18:06.000000000 +0900
@@ -3,6 +3,7 @@
 use ActionDispatch::Static
 use ActionDispatch::Executor
 use Rack::Runtime
+use Rack::MethodOverride
 use ActionDispatch::RequestId
 use ActionDispatch::RemoteIp
 use Rails::Rack::Logger
@@ -12,7 +13,12 @@
 use ActionDispatch::Reloader
 use ActionDispatch::Callbacks
 use ActiveRecord::Migration::CheckPending
+use ActionDispatch::Cookies
+use ActionDispatch::Session::CookieStore
+use ActionDispatch::Flash
+use ActionDispatch::ContentSecurityPolicy::Middleware
 use Rack::Head
 use Rack::ConditionalGet
 use Rack::ETag
+use Rack::TempfileReaper
 run RailsCsrfProtectionApi::Application.routes

セッションストレージについて

Rails 的には CookieStore 推奨っぽい

あらゆるセッションは、セッション固有のIDをcookieに保存します (注意: RailsでセッションIDをURLで渡すことはセキュリティ上の危険があるため許可されません。セッションIDは必ずcookieで渡さなくてはなりません)。

多くのセッションストアでは、このIDは単にサーバー上のセッションデータ (データベーステーブルなど) を検索するために使われています。ただしCookieStoreは例外的にcookie自身にすべてのセッションデータを保存します(必要であればセッションIDも利用できます)。そしてRailsではCookieStoreがデフォルトで使われ、かつRailsでの推奨セッションストアでもあります。CookieStoreの利点は、非常に軽量であることと、新規Webアプリケーションでセッションを利用するための準備がまったく不要である点です。cookieデータは改竄防止のために暗号署名が与えられています。さらにcookie自身も暗号化されているので、内容を他人に読まれないようになっています。(改ざんされたcookieRailsが拒否します)

以下も参考になりそう。

Rails セキュリティガイド#セッションストレージ

参考