diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index c45d947904172290e13b6bb62c3eb904f010a81e..218879554252e9853962d3195cbb17c17bd437d9 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -490,10 +490,14 @@ def request; @parent_jar.request; end private def expiry_options(options) - if options[:expires].respond_to?(:from_now) - { expires_in: options[:expires] } + if request.use_authenticated_cookie_encryption + if options[:expires].respond_to?(:from_now) + { expires_in: options[:expires] } + else + { expires_at: options[:expires] } + end else - { expires_at: options[:expires] } + {} end end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 94cff10fe40ef9d7038152546d9360589135272c..db49bcc87b263c50d2960d7c9aff3988232d418b 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -1283,6 +1283,18 @@ def test_signed_cookie_with_expires_set_relatively assert_nil cookies.signed[:user_name] end + def test_signed_cookie_does_not_embed_expiration_if_config_is_set_to_false + @request.env['action_dispatch.use_authenticated_cookie_encryption'] = false + + cookies.signed[:user_name] = { value: "assain", expires: 2.hours } + + travel 1.hour + assert_equal "assain", cookies.signed[:user_name] + + travel 2.hours + assert_equal "assain", cookies.signed[:user_name] + end + def test_encrypted_cookie_with_expires_set_relatively cookies.encrypted[:user_name] = { value: "assain", expires: 2.hours } @@ -1293,6 +1305,18 @@ def test_encrypted_cookie_with_expires_set_relatively assert_nil cookies.encrypted[:user_name] end + def test_encrypted_cookie_does_not_embed_expiration_if_config_is_set_to_false + @request.env['action_dispatch.use_authenticated_cookie_encryption'] = false + + cookies.encrypted[:user_name] = { value: "assain", expires: 2.hours } + + travel 1.hour + assert_equal "assain", cookies.encrypted[:user_name] + + travel 2.hours + assert_equal "assain", cookies.encrypted[:user_name] + end + def test_vanilla_cookie_with_expires_set_relatively travel_to Time.utc(2017, 8, 15) do get :cookie_expires_in_two_hours diff --git a/guides/source/configuring.md b/guides/source/configuring.md index b0f39e7ab5353cc3232114d05f005008ba0755a8..6ffa6931984170beda41940727395a761326d7ca 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -502,6 +502,10 @@ Defaults to `'signed cookie'`. * `config.action_dispatch.cookies_rotations` allows rotating secrets, ciphers, and digests for encrypted and signed cookies. +* `config.action_dispatch.use_authenticated_cookie_encryption` controls encrypted cookies to use AES-256-GC + authenticated encryption and if signed and encrypted cookies are going to embed the expiry information + in the value. It defaults to `false`. + * `config.action_dispatch.perform_deep_munge` configures whether `deep_munge` method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation) for more information. It defaults to `true`. diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 51b284ff12bf5bd9a91dc4778eb2f5f9840e2d40..ef6f0b52ff3c4e0b6e9ce849c6ff9f2623d02597 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -76,6 +76,17 @@ Rails 5.2 adds bootsnap gem in the [newly generated app's Gemfile](https://githu The `app:update` task sets it up in `boot.rb`. If you want to use it, then add it in the Gemfile, otherwise change the `boot.rb` to not use bootsnap. +### Expiry in signed or encrypted cookie is now embedded in the cookies values + +To improve security, Rails now embeds the expiry information also in encrypted or signed cookies value. + +This new embed information make those cookies incompatible with versions of Rails older than 5.2. + +If you require your cookies to be read by 5.1 and older, or you are still validating your 5.2 deploy and want +to allow you to rollback set +`Rails.application.config.action_dispatch.use_authenticated_cookie_encryption` to `false`. + + Upgrading from Rails 5.0 to Rails 5.1 ------------------------------------- diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt index b4ef4558020f68f444e639a5c50308ad901fe8e7..421e5a2a3acc732f12ceee99ae80b32d9d11083d 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_5_2.rb.tt @@ -11,6 +11,11 @@ # Rails.application.config.active_record.cache_versioning = true # Use AES-256-GCM authenticated encryption for encrypted cookies. +# Also, embed cookie expiry in signed or encrypted cookies for increased security. +# +# This option is not backwards compatible with earlier Rails versions. +# It's best enabled when your entire app is migrated and stable on 5.2. +# # Existing cookies will be converted on read then written with the new scheme. # Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true