diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 85f943814ef7bd5804576975949a1b808c82b738..798c34e87c177a0b496cb2d086fe9383add64489 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,16 @@ ## Rails 4.0.0 (unreleased) ## +* Automatically configure cookie-based sessions to be encrypted if + `secret_key_base` is set, falling back to signed if only `secret_token` + is set. Automatically upgrade existing signed cookie-based sessions from + Rails 3.x to be encrypted if both `secret_key_base` and `secret_token` + are set, or signed with the new key generator if only `secret_token` is + set. This leaves only the `config.session_store :cookie_store` option and + removes the two new options introduced in 4.0.0.beta1: + `encrypted_cookie_store` and `upgrade_signature_to_encryption_cookie_store`. + + *Trevor Turk* + * Ensure consistent fallback to the default layout lookup for layouts set using symbols or procs that return `nil`. diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 618e2f30333acad5617ac1ba7db6d5a1ec71842d..2c88bc3b1f70128effb857449a3fb3120f102f8f 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -84,8 +84,6 @@ module Http module Session autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' - autoload :EncryptedCookieStore, 'action_dispatch/middleware/session/cookie_store' - autoload :UpgradeSignatureToEncryptionCookieStore, 'action_dispatch/middleware/session/cookie_store' autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' autoload :CacheStore, 'action_dispatch/middleware/session/cache_store' end diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index f21d1d4ee5300ea2dc4ad183d0923543ee78eff9..08c75632bac7760431193a2ce0fbe3d6f5cfabf7 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -117,6 +117,9 @@ def permanent # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed # cookie was tampered with by the user (or a 3rd party), nil will be returned. # + # If +config.secret_key_base+ and +config.secret_token+ (deprecated) are both set, + # legacy cookies signed with the old key generator will be transparently upgraded. + # # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. # # Example: @@ -126,23 +129,20 @@ def permanent # # cookies.signed[:discount] # => 45 def signed - @signed ||= begin - if @options[:upgrade_legacy_signed_cookie_jar] + @signed ||= + if @options[:upgrade_legacy_signed_cookies] UpgradeLegacySignedCookieJar.new(self, @key_generator, @options) else SignedCookieJar.new(self, @key_generator, @options) end - end - end - - # Only needed for supporting the +UpgradeSignatureToEncryptionCookieStore+, users and plugin authors should not use this - def signed_using_old_secret #:nodoc: - @signed_using_old_secret ||= SignedCookieJar.new(self, ActiveSupport::DummyKeyGenerator.new(@options[:secret_token]), @options) end # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read. # If the cookie was tampered with by the user (or a 3rd party), nil will be returned. # + # If +config.secret_key_base+ and +config.secret_token+ (deprecated) are both set, + # legacy cookies signed with the old key generator will be transparently upgraded. + # # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. # # Example: @@ -152,7 +152,38 @@ def signed_using_old_secret #:nodoc: # # cookies.encrypted[:discount] # => 45 def encrypted - @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options) + @encrypted ||= + if @options[:upgrade_legacy_signed_cookies] + UpgradeLegacyEncryptedCookieJar.new(self, @key_generator, @options) + else + EncryptedCookieJar.new(self, @key_generator, @options) + end + end + + # Returns the +signed+ or +encrypted jar, preferring +encrypted+ if +secret_key_base+ is set. + # Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores. + def signed_or_encrypted + @signed_or_encrypted ||= + if @options[:secret_key_base].present? + encrypted + else + signed + end + end + end + + module VerifyAndUpgradeLegacySignedMessage + def initialize(*args) + super + @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token]) + end + + def verify_and_upgrade_legacy_signed_message(name, signed_message) + @legacy_verifier.verify(signed_message).tap do |value| + self[name] = value + end + rescue ActiveSupport::MessageVerifier::InvalidSignature + nil end end @@ -179,7 +210,7 @@ def self.options_for_env(env) #:nodoc: encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '', secret_token: env[SECRET_TOKEN], secret_key_base: env[SECRET_KEY_BASE], - upgrade_legacy_signed_cookie_jar: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present? + upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present? } end @@ -354,10 +385,8 @@ def initialize(parent_jar, key_generator, options = {}) def [](name) if signed_message = @parent_jar[name] - @verifier.verify(signed_message) + verify(signed_message) end - rescue ActiveSupport::MessageVerifier::InvalidSignature - nil end def []=(key, options) @@ -371,6 +400,14 @@ def []=(key, options) raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE @parent_jar[key] = options end + + private + + def verify(signed_message) + @verifier.verify(signed_message) + rescue ActiveSupport::MessageVerifier::InvalidSignature + nil + end end # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if @@ -378,30 +415,13 @@ def []=(key, options) # legacy cookies signed with the old dummy key generator and re-saves # them using the new key generator to provide a smooth upgrade path. class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc: - def initialize(*args) - super - @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token]) - end + include VerifyAndUpgradeLegacySignedMessage def [](name) if signed_message = @parent_jar[name] - verify_signed_message(signed_message) || verify_and_upgrade_legacy_signed_message(name, signed_message) + verify(signed_message) || verify_and_upgrade_legacy_signed_message(name, signed_message) end end - - def verify_signed_message(signed_message) - @verifier.verify(signed_message) - rescue ActiveSupport::MessageVerifier::InvalidSignature - nil - end - - def verify_and_upgrade_legacy_signed_message(name, signed_message) - @legacy_verifier.verify(signed_message).tap do |value| - self[name] = value - end - rescue ActiveSupport::MessageVerifier::InvalidSignature - nil - end end class EncryptedCookieJar #:nodoc: @@ -409,8 +429,8 @@ class EncryptedCookieJar #:nodoc: def initialize(parent_jar, key_generator, options = {}) if ActiveSupport::DummyKeyGenerator === key_generator - raise "Encrypted Cookies must be used in conjunction with config.secret_key_base." + - "Set config.secret_key_base in config/initializers/secret_token.rb" + raise "You didn't set config.secret_key_base, which is required for this cookie jar. " + + "Read the upgrade documentation to learn more about this new config option." end @parent_jar = parent_jar @@ -422,11 +442,8 @@ def initialize(parent_jar, key_generator, options = {}) def [](key) if encrypted_message = @parent_jar[key] - @encryptor.decrypt_and_verify(encrypted_message) + decrypt_and_verify(encrypted_message) end - rescue ActiveSupport::MessageVerifier::InvalidSignature, - ActiveSupport::MessageEncryptor::InvalidMessage - nil end def []=(key, options) @@ -440,6 +457,28 @@ def []=(key, options) raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE @parent_jar[key] = options end + + private + + def decrypt_and_verify(encrypted_message) + @encryptor.decrypt_and_verify(encrypted_message) + rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage + nil + end + end + + # UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore + # instead of EncryptedCookieJar if config.secret_token and config.secret_key_base + # are both set. It reads legacy cookies signed with the old dummy key generator and + # encrypts and re-saves them using the new key generator to provide a smooth upgrade path. + class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc: + include VerifyAndUpgradeLegacySignedMessage + + def [](name) + if encrypted_or_signed_message = @parent_jar[name] + decrypt_and_verify(encrypted_or_signed_message) || verify_and_upgrade_legacy_signed_message(name, encrypted_or_signed_message) + end + end end def initialize(app) diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 1e6ed624b0f621e1716788073519d5be11862804..a603b33b45a82d465ab0bb626214381cc979733f 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -100,42 +100,7 @@ def get_cookie(env) def cookie_jar(env) request = ActionDispatch::Request.new(env) - request.cookie_jar.signed - end - end - - class EncryptedCookieStore < CookieStore - - private - - def cookie_jar(env) - request = ActionDispatch::Request.new(env) - request.cookie_jar.encrypted - end - end - - # This cookie store helps you upgrading apps that use +CookieStore+ to the new default +EncryptedCookieStore+ - # To use this CookieStore set - # - # Myapp::Application.config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session' - # - # in your config/initializers/session_store.rb - # - # You will also need to add - # - # Myapp::Application.config.secret_key_base = 'some secret' - # - # in your config/initializers/secret_token.rb, but do not remove +Myapp::Application.config.secret_token = 'some secret'+ - class UpgradeSignatureToEncryptionCookieStore < EncryptedCookieStore - private - - def get_cookie(env) - signed_using_old_secret_cookie_jar(env)[@key] || cookie_jar(env)[@key] - end - - def signed_using_old_secret_cookie_jar(env) - request = ActionDispatch::Request.new(env) - request.cookie_jar.signed_using_old_secret + request.cookie_jar.signed_or_encrypted end end end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index c532e0b8cc4c8af96b4eac7767b877e1f3a0bc93..68034e7f7bd7f856c7b901d6f4d7e9b0a9f3c7ff 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -86,6 +86,11 @@ def set_encrypted_cookie head :ok end + def get_encrypted_cookie + cookies.encrypted[:foo] + head :ok + end + def set_invalid_encrypted_cookie cookies[:invalid_cookie] = 'invalid--9170e00a57cfc27083363b5c75b835e477bd90cf' head :ok @@ -456,7 +461,42 @@ def test_signed_uses_upgrade_legacy_signed_cookie_jar_if_both_secret_token_and_s assert_kind_of ActionDispatch::Cookies::UpgradeLegacySignedCookieJar, cookies.signed end - def test_legacy_signed_cookie_is_read_and_transparently_upgraded_if_both_secret_token_and_secret_key_base_are_set + def test_signed_or_encrypted_uses_signed_cookie_jar_if_only_secret_token_is_set + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = nil + get :get_encrypted_cookie + assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed_or_encrypted + end + + def test_signed_or_encrypted_uses_encrypted_cookie_jar_if_only_secret_key_base_is_set + @request.env["action_dispatch.secret_token"] = nil + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + get :get_encrypted_cookie + assert_kind_of ActionDispatch::Cookies::EncryptedCookieJar, cookies.signed_or_encrypted + end + + def test_signed_or_encrypted_uses_upgrade_legacy_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + get :get_encrypted_cookie + assert_kind_of ActionDispatch::Cookies::UpgradeLegacyEncryptedCookieJar, cookies.signed_or_encrypted + end + + def test_encrypted_uses_encrypted_cookie_jar_if_only_secret_key_base_is_set + @request.env["action_dispatch.secret_token"] = nil + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + get :get_encrypted_cookie + assert_kind_of ActionDispatch::Cookies::EncryptedCookieJar, cookies.encrypted + end + + def test_encrypted_uses_upgrade_legacy_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + get :get_encrypted_cookie + assert_kind_of ActionDispatch::Cookies::UpgradeLegacyEncryptedCookieJar, cookies.encrypted + end + + def test_legacy_signed_cookie_is_read_and_transparently_upgraded_by_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" @@ -473,7 +513,27 @@ def test_legacy_signed_cookie_is_read_and_transparently_upgraded_if_both_secret_ assert_equal 45, verifier.verify(@response.cookies["user_id"]) end - def test_legacy_signed_cookie_is_nil_if_tampered + def test_legacy_signed_cookie_is_read_and_transparently_encrypted_by_encrypted_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + @request.env["action_dispatch.encrypted_cookie_salt"] = "4433796b79d99a7735553e316522acee" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "00646eb40062e1b1deff205a27cd30f9" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate('bar') + + @request.headers["Cookie"] = "foo=#{legacy_value}" + get :get_encrypted_cookie + + assert_equal 'bar', @controller.send(:cookies).encrypted[:foo] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_cookie_salt"]) + sign_secret = key_generator.generate_key(@request.env["action_dispatch.encrypted_signed_cookie_salt"]) + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) + assert_equal 'bar', encryptor.decrypt_and_verify(@response.cookies["foo"]) + end + + def test_legacy_signed_cookie_is_treated_as_nil_by_signed_cookie_jar_if_tampered @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" @@ -484,6 +544,17 @@ def test_legacy_signed_cookie_is_nil_if_tampered assert_equal nil, @response.cookies["user_id"] end + def test_legacy_signed_cookie_is_treated_as_nil_by_encrypted_cookie_jar_if_tampered + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + @request.headers["Cookie"] = "foo=baz" + get :get_encrypted_cookie + + assert_equal nil, @controller.send(:cookies).encrypted[:foo] + assert_equal nil, @response.cookies["foo"] + end + def test_cookie_with_all_domain_option get :set_cookie_with_domain assert_response :success diff --git a/guides/code/getting_started/config/initializers/session_store.rb b/guides/code/getting_started/config/initializers/session_store.rb index 2e37d9379978807758404eb2d75ccf0b4776633a..3b2ca93ab99d0af01d254668a428aba57523d9af 100644 --- a/guides/code/getting_started/config/initializers/session_store.rb +++ b/guides/code/getting_started/config/initializers/session_store.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Blog::Application.config.session_store :encrypted_cookie_store, key: '_blog_session' +Blog::Application.config.session_store :cookie_store, key: '_blog_session' diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index a2ba5dd062c7d0611d662501f97cb09649494e59..83134fcf87c7a3ece54330b7b70e98b774155bae 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -92,17 +92,20 @@ Rails 4.0 extracted Active Resource to its own gem. If you still need the featur Please note that you should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. This is because cookies signed based on the new `secret_key_base` in Rails 4.x are not backwards compatible with Rails 3.x. You are free to leave your existing `secret_token` in place, not set the new `secret_key_base`, and ignore the deprecation warnings until you are reasonably sure that your upgrade is otherwise complete. -* Rails 4.0 introduces a new `UpgradeSignatureToEncryptionCookieStore` cookie store. This is useful for upgrading apps using the old default `CookieStore` to the new default `EncryptedCookieStore` which leverages the new `ActiveSupport::KeyGenerator`. To use this transitional cookie store, you'll want to leave your existing `secret_token` in place, add a new `secret_key_base`, and change your `session_store` like so: +If you are relying on the ability for external applications or Javascript to be able to read your Rails app's signed session cookies (or signed cookies in general) you should not set `secret_key_base` until you have decoupled these concerns. -```ruby - # config/initializers/session_store.rb - Myapp::Application.config.session_store :upgrade_signature_to_encryption_cookie_store, key: 'existing session key' +* Rails 4.0 encrypts the contents of cookie-based sessions if `secret_key_base` has been set. Rails 3.x signed, but did not encrypt, the contents of cookie-based session. Signed cookies are "secure" in that they are verified to have been generated by your app and are tamper-proof. However, the contents can be viewed by end users, and encrypting the contents eliminates this caveat/concern. + +As described above, existing signed cookies generated with Rails 3.x will be transparently upgraded if you leave your existing `secret_token` in place and add the new `secret_key_base`. +```ruby # config/initializers/secret_token.rb Myapp::Application.config.secret_token = 'existing secret token' Myapp::Application.config.secret_key_base = 'new secret key base' ``` +The same caveats apply here, too. You should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. You should also take care to make sure you are not relying on the ability to decode signed cookies generated by your app in external applications or Javascript before upgrading. + * Rails 4.0 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature. * Rails 4.0 has deprecated `ActionController::Base.page_cache_extension` option. Use `ActionController::Base.default_static_extension` instead. diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 563905e8b3b9729533ca0a3ce291277ca171575d..455ceed5f814f6c88d5397219ef77e28abd83268 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,4 +1,5 @@ require 'fileutils' +require 'active_support/core_ext/object/blank' # FIXME remove DummyKeyGenerator and this require in 4.1 require 'active_support/key_generator' require 'rails/engine' @@ -122,7 +123,8 @@ def key_generator # # * "action_dispatch.parameter_filter" => config.filter_parameters # * "action_dispatch.redirect_filter" => config.filter_redirect - # * "action_dispatch.secret_token" => config.secret_token, + # * "action_dispatch.secret_token" => config.secret_token + # * "action_dispatch.secret_key_base" => config.secret_key_base # * "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions # * "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local # * "action_dispatch.logger" => Rails.logger @@ -135,13 +137,12 @@ def key_generator # def env_config @app_env_config ||= begin - if config.secret_key_base.nil? - ActiveSupport::Deprecation.warn "You didn't set config.secret_key_base in config/initializers/secret_token.rb file. " + - "This should be used instead of the old deprecated config.secret_token in order to use the new EncryptedCookieStore. " + - "To convert safely to the encrypted store (without losing existing cookies and sessions), see http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#action-pack" + if config.secret_key_base.blank? + ActiveSupport::Deprecation.warn "You didn't set config.secret_key_base. " + + "Read the upgrade documentation to learn more about this new config option." if config.secret_token.blank? - raise "You must set config.secret_key_base in your app's config" + raise "You must set config.secret_key_base in your app's config." end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt index df07de9922c8b010d43fb1418ca56a8c3e2e8aaf..4a099a4ce281c3c2e846a3aa46c42e86ed932abb 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -<%= app_const %>.config.session_store :encrypted_cookie_store, key: <%= "'_#{app_name}_session'" %> +<%= app_const %>.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %> diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index a5fdfbf887595c95e61d625f5ff06d077e113cc1..8cb0dfeb63a1337ee25d8b12af4ec08df4408cf2 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -157,10 +157,6 @@ def read_raw_cookie end RUBY - add_to_config <<-RUBY - config.session_store :encrypted_cookie_store, key: '_myapp_session' - RUBY - require "#{app_path}/config/environment" get '/foo/write_session' @@ -178,7 +174,7 @@ def read_raw_cookie assert_equal 1, encryptor.decrypt_and_verify(last_response.body)['foo'] end - test "session using upgrade signature to encryption cookie store works the same way as encrypted cookie store" do + test "session upgrading signature to encryption cookie store works the same way as encrypted cookie store" do app_file 'config/routes.rb', <<-RUBY AppTemplate::Application.routes.draw do get ':controller(/:action)' @@ -208,7 +204,6 @@ def read_raw_cookie add_to_config <<-RUBY config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" - config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session' RUBY require "#{app_path}/config/environment" @@ -228,7 +223,7 @@ def read_raw_cookie assert_equal 1, encryptor.decrypt_and_verify(last_response.body)['foo'] end - test "session using upgrade signature to encryption cookie store upgrades session to encrypted mode" do + test "session upgrading signature to encryption cookie store upgrades session to encrypted mode" do app_file 'config/routes.rb', <<-RUBY AppTemplate::Application.routes.draw do get ':controller(/:action)' @@ -264,7 +259,6 @@ def read_raw_cookie add_to_config <<-RUBY config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" - config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session' RUBY require "#{app_path}/config/environment" @@ -287,5 +281,63 @@ def read_raw_cookie get '/foo/read_raw_cookie' assert_equal 2, encryptor.decrypt_and_verify(last_response.body)['foo'] end + + test "session upgrading legacy signed cookies to new signed cookies" do + app_file 'config/routes.rb', <<-RUBY + AppTemplate::Application.routes.draw do + get ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + def write_raw_session + # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1} + cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749" + render nothing: true + end + + def write_session + session[:foo] = session[:foo] + 1 + render nothing: true + end + + def read_session + render text: session[:foo] + end + + def read_signed_cookie + render text: cookies.signed[:_myapp_session]['foo'] + end + + def read_raw_cookie + render text: cookies[:_myapp_session] + end + end + RUBY + + add_to_config <<-RUBY + config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" + config.secret_key_base = nil + RUBY + + require "#{app_path}/config/environment" + + get '/foo/write_raw_session' + get '/foo/read_session' + assert_equal '1', last_response.body + + get '/foo/write_session' + get '/foo/read_session' + assert_equal '2', last_response.body + + get '/foo/read_signed_cookie' + assert_equal '2', last_response.body + + verifier = ActiveSupport::MessageVerifier.new(app.config.secret_token) + + get '/foo/read_raw_cookie' + assert_equal 2, verifier.verify(last_response.body)['foo'] + end end end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 51e91656c01873e4d0418c1e1545d4f25599379c..5fdf58c8eed2ea1b62917bb0bacc6bee688ee3cf 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -346,7 +346,7 @@ def test_no_active_record_or_test_unit_if_skips_given def test_new_hash_style run_generator [destination_root] assert_file "config/initializers/session_store.rb" do |file| - assert_match(/config.session_store :encrypted_cookie_store, key: '_.+_session'/, file) + assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file) end end