diff options
author | Santiago Pastorino <santiago@wyeworks.com> | 2012-11-16 17:17:08 -0200 |
---|---|---|
committer | Santiago Pastorino <santiago@wyeworks.com> | 2012-11-16 17:29:26 -0200 |
commit | 8eefdb6d7056dc0d4d63a5c34a4b12701ba21c88 (patch) | |
tree | 18e732ac6913c5ee1cc211b19ce54396d569f3ef | |
parent | d4b9a3fa706034b1ac2a6f8a479a697d071517a9 (diff) | |
download | rails-8eefdb6d7056dc0d4d63a5c34a4b12701ba21c88.tar.gz rails-8eefdb6d7056dc0d4d63a5c34a4b12701ba21c88.tar.bz2 rails-8eefdb6d7056dc0d4d63a5c34a4b12701ba21c88.zip |
Add UpgradeSignatureToEncryptionCookieStore
This allows easy upgrading from the old signed Cookie Store <= 3.2
or the deprecated one in 4.0 (the ones that doesn't use key derivation)
to the new one that signs using key derivation
-rw-r--r-- | actionpack/lib/action_dispatch.rb | 11 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/cookies.rb | 10 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/session/cookie_store.rb | 17 | ||||
-rw-r--r-- | railties/lib/rails/application.rb | 2 | ||||
-rw-r--r-- | railties/test/application/middleware/session_test.rb | 110 |
5 files changed, 143 insertions, 7 deletions
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index ab1755acd5..1d716a3248 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -81,11 +81,12 @@ module ActionDispatch end 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 :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' - autoload :CacheStore, 'action_dispatch/middleware/session/cache_store' + 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 mattr_accessor :test_app diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 7936dcb515..2f148752cb 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -85,7 +85,7 @@ module ActionDispatch SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt".freeze ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze - + TOKEN_KEY = "action_dispatch.secret_token".freeze # Raised when storing more than 4K of session data. CookieOverflow = Class.new StandardError @@ -112,7 +112,8 @@ module ActionDispatch key_generator = env[GENERATOR_KEY] options = { signed_cookie_salt: env[SIGNED_COOKIE_SALT], encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT], - encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] } + encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT], + token_key: env[TOKEN_KEY] } host = request.host secure = request.ssl? @@ -251,6 +252,11 @@ module ActionDispatch @signed ||= SignedCookieJar.new(self, @key_generator, @options) 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[:token_key]), @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), an ActiveSupport::MessageVerifier::InvalidSignature exception # will be raised. diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 55a9314524..d7f83a1cc6 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -93,5 +93,22 @@ module ActionDispatch 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.config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session' + # in your config/initializers/session_store.rb + 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 + end + end end end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index b7844bbfbe..ae3993fbd8 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -123,6 +123,7 @@ module Rails # Currently stores: # # * "action_dispatch.parameter_filter" => config.filter_parameters + # * "action_dispatch.secret_token" => config.secret_token, # * "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions # * "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local # * "action_dispatch.logger" => Rails.logger @@ -148,6 +149,7 @@ module Rails super.merge({ "action_dispatch.parameter_filter" => config.filter_parameters, + "action_dispatch.secret_token" => config.secret_token, "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions, "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local, "action_dispatch.logger" => Rails.logger, diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index 9c0b143b74..a5fdfbf887 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -177,5 +177,115 @@ module ApplicationTests get '/foo/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 + 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_session + session[:foo] = 1 + render nothing: true + end + + def read_session + render text: session[:foo] + end + + def read_encrypted_cookie + render text: cookies.encrypted[:_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.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session' + RUBY + + require "#{app_path}/config/environment" + + get '/foo/write_session' + get '/foo/read_session' + assert_equal '1', last_response.body + + get '/foo/read_encrypted_cookie' + assert_equal '1', last_response.body + + secret = app.key_generator.generate_key('encrypted cookie') + sign_secret = app.key_generator.generate_key('signed encrypted cookie') + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) + + get '/foo/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 + 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_encrypted_cookie + render text: cookies.encrypted[:_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.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session' + 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_encrypted_cookie' + assert_equal '2', last_response.body + + secret = app.key_generator.generate_key('encrypted cookie') + sign_secret = app.key_generator.generate_key('signed encrypted cookie') + encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) + + get '/foo/read_raw_cookie' + assert_equal 2, encryptor.decrypt_and_verify(last_response.body)['foo'] + end end end |