From 60609bb50d5b99d78a01a945a539cccd061cd7e7 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 31 Oct 2012 01:06:46 -0200 Subject: Sign cookies using key deriver --- .../metal/request_forgery_protection.rb | 8 ++--- .../lib/action_dispatch/middleware/cookies.rb | 34 ++++++++++++---------- actionpack/test/controller/flash_test.rb | 6 ++-- actionpack/test/dispatch/cookies_test.rb | 14 +++++---- .../test/dispatch/session/cookie_store_test.rb | 5 +++- activesupport/lib/active_support/key_generator.rb | 10 +++++++ .../config/initializers/secret_token.rb | 2 +- guides/source/action_controller_overview.md | 2 +- guides/source/configuring.md | 2 +- railties/lib/rails/application.rb | 9 +++++- railties/lib/rails/application/configuration.rb | 10 +++++-- .../config/initializers/secret_token.rb.tt | 4 +-- railties/test/application/configuration_test.rb | 24 +++++++++++++-- railties/test/application/url_generation_test.rb | 2 +- railties/test/isolation/abstract_unit.rb | 4 +-- 15 files changed, 95 insertions(+), 41 deletions(-) diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index a50f0ca8c1..265ce5d6f3 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -121,11 +121,11 @@ module ActionController #:nodoc: class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc: def self.build(request) - secret = request.env[ActionDispatch::Cookies::TOKEN_KEY] - host = request.host - secure = request.ssl? + key_generator = request.env[ActionDispatch::Cookies::GENERATOR_KEY] + host = request.host + secure = request.ssl? - new(secret, host, secure) + new(key_generator, host, secure) end def write(*) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index eaf922595a..0ec7f24a14 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -27,7 +27,7 @@ module ActionDispatch # cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now } # # # Sets a signed cookie, which prevents users from tampering with its value. - # # The cookie is signed by your app's config.secret_token value. + # # The cookie is signed by your app's config.secret_token_key value. # # It can be read using the signed method cookies.signed[:key] # cookies.signed[:user_id] = current_user.id # @@ -79,8 +79,8 @@ module ActionDispatch # * :httponly - Whether this cookie is accessible via scripting or # only HTTP. Defaults to +false+. class Cookies - HTTP_HEADER = "Set-Cookie".freeze - TOKEN_KEY = "action_dispatch.secret_token".freeze + HTTP_HEADER = "Set-Cookie".freeze + GENERATOR_KEY = "action_dispatch.key_generator".freeze # Raised when storing more than 4K of session data. CookieOverflow = Class.new StandardError @@ -103,17 +103,19 @@ module ActionDispatch DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/ def self.build(request) - secret = request.env[TOKEN_KEY] + env = request.env + key_generator = env[GENERATOR_KEY] + host = request.host secure = request.ssl? - new(secret, host, secure).tap do |hash| + new(key_generator, host, secure).tap do |hash| hash.update(request.cookies) end end - def initialize(secret = nil, host = nil, secure = false) - @secret = secret + def initialize(key_generator, host = nil, secure = false) + @key_generator = key_generator @set_cookies = {} @delete_cookies = {} @host = host @@ -220,7 +222,7 @@ module ActionDispatch # cookies.permanent.signed[:remember_me] = current_user.id # # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT def permanent - @permanent ||= PermanentCookieJar.new(self, @secret) + @permanent ||= PermanentCookieJar.new(self, @key_generator) end # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from @@ -228,7 +230,7 @@ module ActionDispatch # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will # be raised. # - # This jar requires that you set a suitable secret for the verification on your app's +config.secret_token+. + # This jar requires that you set a suitable secret for the verification on your app's +config.secret_token_key+. # # Example: # @@ -237,7 +239,7 @@ module ActionDispatch # # cookies.signed[:discount] # => 45 def signed - @signed ||= SignedCookieJar.new(self, @secret) + @signed ||= SignedCookieJar.new(self, @key_generator) end def write(headers) @@ -261,8 +263,9 @@ module ActionDispatch end class PermanentCookieJar < CookieJar #:nodoc: - def initialize(parent_jar, secret) - @parent_jar, @secret = parent_jar, secret + def initialize(parent_jar, key_generator) + @parent_jar = parent_jar + @key_generator = key_generator end def []=(key, options) @@ -285,9 +288,10 @@ module ActionDispatch MAX_COOKIE_SIZE = 4096 # Cookies can typically store 4096 bytes. SECRET_MIN_LENGTH = 30 # Characters - def initialize(parent_jar, secret) - ensure_secret_secure(secret) + def initialize(parent_jar, key_generator) @parent_jar = parent_jar + secret = key_generator.generate_key('signed cookie') + ensure_secret_secure(secret) @verifier = ActiveSupport::MessageVerifier.new(secret) end @@ -323,7 +327,7 @@ module ActionDispatch if secret.blank? raise ArgumentError, "A secret is required to generate an " + "integrity hash for cookie session data. Use " + - "config.secret_token = \"some secret phrase of at " + + "config.secret_token_key = \"some secret phrase of at " + "least #{SECRET_MIN_LENGTH} characters\"" + "in config/initializers/secret_token.rb" end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 8340aab4d2..d8c78db8c0 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +# FIXME remove DummyKeyGenerator and this require in 4.1 +require 'active_support/key_generator' class FlashTest < ActionController::TestCase class TestController < ActionController::Base @@ -217,7 +219,7 @@ end class FlashIntegrationTest < ActionDispatch::IntegrationTest SessionKey = '_myapp_session' - SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33' + Generator = ActiveSupport::DummyKeyGenerator.new('b3c631c314c0bbca50c1b2843150fe33') class TestController < ActionController::Base add_flash_types :bar @@ -291,7 +293,7 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest # Overwrite get to send SessionSecret in env hash def get(path, parameters = nil, env = {}) - env["action_dispatch.secret_token"] ||= SessionSecret + env["action_dispatch.key_generator"] ||= Generator super end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 347b3b3b5a..fc0db8b91d 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +# FIXME remove DummyKeyGenerator and this require in 4.1 +require 'active_support/key_generator' class CookiesTest < ActionController::TestCase class TestController < ActionController::Base @@ -146,7 +148,7 @@ class CookiesTest < ActionController::TestCase def setup super - @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") @request.host = "www.nextangle.com" end @@ -329,29 +331,29 @@ class CookiesTest < ActionController::TestCase def test_raises_argument_error_if_missing_secret assert_raise(ArgumentError, nil.inspect) { - @request.env["action_dispatch.secret_token"] = nil + @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new(nil) get :set_signed_cookie } assert_raise(ArgumentError, ''.inspect) { - @request.env["action_dispatch.secret_token"] = "" + @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new("") get :set_signed_cookie } end def test_raises_argument_error_if_secret_is_probably_insecure assert_raise(ArgumentError, "password".inspect) { - @request.env["action_dispatch.secret_token"] = "password" + @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new("password") get :set_signed_cookie } assert_raise(ArgumentError, "secret".inspect) { - @request.env["action_dispatch.secret_token"] = "secret" + @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new("secret") get :set_signed_cookie } assert_raise(ArgumentError, "12345678901234567890123456789".inspect) { - @request.env["action_dispatch.secret_token"] = "12345678901234567890123456789" + @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new("12345678901234567890123456789") get :set_signed_cookie } end diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 41fa036a92..1677dee524 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -1,9 +1,12 @@ require 'abstract_unit' require 'stringio' +# FIXME remove DummyKeyGenerator and this require in 4.1 +require 'active_support/key_generator' class CookieStoreTest < ActionDispatch::IntegrationTest SessionKey = '_myapp_session' SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33' + Generator = ActiveSupport::DummyKeyGenerator.new(SessionSecret) Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, :digest => 'SHA1') SignedBar = Verifier.generate(:foo => "bar", :session_id => SecureRandom.hex(16)) @@ -330,7 +333,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest # Overwrite get to send SessionSecret in env hash def get(path, parameters = nil, env = {}) - env["action_dispatch.secret_token"] ||= SessionSecret + env["action_dispatch.key_generator"] ||= Generator super end diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 04d170f801..8b49ad8414 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -20,4 +20,14 @@ module ActiveSupport OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size) end end + + class DummyKeyGenerator + def initialize(secret) + @secret = secret + end + + def generate_key(salt) + @secret + end + end end diff --git a/guides/code/getting_started/config/initializers/secret_token.rb b/guides/code/getting_started/config/initializers/secret_token.rb index f36ebdda18..8d52c11b69 100644 --- a/guides/code/getting_started/config/initializers/secret_token.rb +++ b/guides/code/getting_started/config/initializers/secret_token.rb @@ -6,4 +6,4 @@ # no regular words or you'll be exposed to dictionary attacks. # Make sure your secret key is kept private # if you're sharing your code publicly. -Blog::Application.config.secret_token = '685a9bf865b728c6549a191c90851c1b5ec41ecb60b9e94ad79dd3f824749798aa7b5e94431901960bee57809db0947b481570f7f13376b7ca190fa28099c459' +Blog::Application.config.secret_token_key = '685a9bf865b728c6549a191c90851c1b5ec41ecb60b9e94ad79dd3f824749798aa7b5e94431901960bee57809db0947b481570f7f13376b7ca190fa28099c459' diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index dd8d229e6a..d5d74ca3f5 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -219,7 +219,7 @@ Rails sets up (for the CookieStore) a secret key used for signing the session da # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -YourApp::Application.config.secret_token = '49d3f3de9ed86c74b94ad6bd0...' +YourApp::Application.config.secret_token_key = '49d3f3de9ed86c74b94ad6bd0...' ``` NOTE: Changing the secret when using the `CookieStore` will invalidate all existing sessions. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 0b4f183d61..cadc09ae33 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -113,7 +113,7 @@ These configuration methods are to be called on a `Rails::Railtie` object, such * `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If `config.cache_classes` is true, this option is ignored. -* `config.secret_token` used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_token` initialized to a random key in `config/initializers/secret_token.rb`. +* `config.secret_token_key` used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_token_key` initialized to a random key in `config/initializers/secret_token.rb`. * `config.serve_static_assets` configures Rails itself to serve static assets. Defaults to true, but in the production environment is turned off as the server software (e.g. Nginx or Apache) used to run the application should serve static assets instead. Unlike the default setting set this to true when running (absolutely not recommended!) or testing your app in production mode using WEBrick. Otherwise you won´t be able use page caching and requests for files that exist regularly under the public directory will anyway hit your Rails app. diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 9ef001c7d0..f22025d35e 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,5 +1,7 @@ require 'fileutils' require 'active_support/queueing' +# FIXME remove DummyKeyGenerator and this require in 4.1 +require 'active_support/key_generator' require 'rails/engine' module Rails @@ -106,7 +108,11 @@ module Rails def key_generator # number of iterations selected based on consultation with the google security # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220 - @key_generator ||= ActiveSupport::KeyGenerator.new(config.secret_token, iterations: 1000) + @key_generator ||= if config.secret_token_key + ActiveSupport::KeyGenerator.new(config.secret_token_key, iterations: 1000) + else + ActiveSupport::DummyKeyGenerator.new(config.secret_token) + end end # Stores some of the Rails initial environment parameters which @@ -119,6 +125,7 @@ module Rails # * "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local, # * "action_dispatch.logger" => Rails.logger, # * "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner + # * "action_dispatch.key_generator" => key_generator # # These parameters will be used by middlewares and engines to configure themselves # diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index cc21213f1c..b01b97aa67 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -10,12 +10,12 @@ module Rails :cache_classes, :cache_store, :consider_all_requests_local, :console, :eager_load, :exceptions_app, :file_watcher, :filter_parameters, :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags, - :railties_order, :relative_url_root, :secret_token, + :railties_order, :relative_url_root, :secret_token_key, :serve_static_assets, :ssl_options, :static_cache_control, :session_options, :time_zone, :reload_classes_only_on_change, :queue, :queue_consumer, :beginning_of_week - attr_writer :log_level + attr_writer :secret_token, :log_level attr_reader :encoding def initialize(*) @@ -46,6 +46,8 @@ module Rails @queue = ActiveSupport::SynchronousQueue.new @queue_consumer = nil @eager_load = nil + @secret_token = nil + @secret_token_key = nil @assets = ActiveSupport::OrderedOptions.new @assets.enabled = false @@ -144,6 +146,10 @@ module Rails def whiny_nils=(*) ActiveSupport::Deprecation.warn "config.whiny_nils option is deprecated and no longer works" end + + def secret_token + @secret_token_key || @secret_token + end end end end diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt index 3c5611ca59..d96185ae2a 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt @@ -7,6 +7,6 @@ # no regular words or you'll be exposed to dictionary attacks. # You can use `rake secret` to generate a secure secret key. -# Make sure your secret_token is kept private +# Make sure your secret_token_key is kept private # if you're sharing your code publicly. -<%= app_const %>.config.secret_token = '<%= app_secret %>' +<%= app_const %>.config.secret_token_key = '<%= app_secret %>' diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c4c1100f19..daf9dd3505 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -225,9 +225,9 @@ module ApplicationTests assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path end - test "config.secret_token is sent in env" do + test "config.secret_token_key is sent in env" do make_basic_app do |app| - app.config.secret_token = 'b3c631c314c0bbca50c1b2843150fe33' + app.config.secret_token_key = 'b3c631c314c0bbca50c1b2843150fe33' app.config.session_store :disabled end @@ -242,6 +242,26 @@ module ApplicationTests assert_equal 'b3c631c314c0bbca50c1b2843150fe33', last_response.body end + test "Use key_generator when secret_token_key is set" do + make_basic_app do |app| + app.config.secret_token_key = 'b3c631c314c0bbca50c1b2843150fe33' + app.config.session_store :disabled + end + + class ::OmgController < ActionController::Base + def index + cookies.signed[:some_key] = "some_value" + render text: cookies[:some_key] + end + end + + get "/" + + secret = app.key_generator.generate_key('signed cookie') + verifier = ActiveSupport::MessageVerifier.new(secret) + assert_equal 'some_value', verifier.verify(last_response.body) + end + test "protect from forgery is the default in a new app" do make_basic_app diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb index 2a48adae5c..fb83659b0c 100644 --- a/railties/test/application/url_generation_test.rb +++ b/railties/test/application/url_generation_test.rb @@ -14,7 +14,7 @@ module ApplicationTests require "action_controller/railtie" class MyApp < Rails::Application - config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" + config.secret_token_key = "3b7cd727ee24e8444053437c36cc66c4" config.session_store :cookie_store, key: "_myapp_session" config.active_support.deprecation = :log config.eager_load = false diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index e59488f97d..2c92f2ded5 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -119,7 +119,7 @@ module TestHelpers add_to_config <<-RUBY config.eager_load = false - config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" + config.secret_token_key = "3b7cd727ee24e8444053437c36cc66c4" config.session_store :cookie_store, key: "_myapp_session" config.active_support.deprecation = :log config.action_controller.allow_forgery_protection = false @@ -138,7 +138,7 @@ module TestHelpers app = Class.new(Rails::Application) app.config.eager_load = false - app.config.secret_token = "3b7cd727ee24e8444053437c36cc66c4" + app.config.secret_token_key = "3b7cd727ee24e8444053437c36cc66c4" app.config.session_store :cookie_store, key: "_myapp_session" app.config.active_support.deprecation = :log -- cgit v1.2.3 From e272000c80548c3de9380bb6c76397d018fb1c68 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 1 Nov 2012 02:20:16 -0200 Subject: Warn config.derive_keys will be true by default in 4.1 --- railties/lib/rails/application.rb | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index f22025d35e..741b03d80e 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -130,15 +130,23 @@ module Rails # These parameters will be used by middlewares and engines to configure themselves # def env_config - @env_config ||= 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, - "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner, - "action_dispatch.key_generator" => key_generator - }) + @env_config ||= begin + if config.secret_token_key.nil? + ActiveSupport::Deprecation.warn "You didn't set config.secret_token_key. " + + "This should be used instead of the old deprecated config.secret_token. " + + "Set config.secret_token_key instead of config.secret_token in config/initializers/secret_token.rb" + end + + 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, + "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner, + "action_dispatch.key_generator" => key_generator + }) + end end ## Rails internal API -- cgit v1.2.3 From 38c40dbbc1de5837a05d762be95e69105acc929c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 30 Oct 2012 16:41:11 -0200 Subject: Add cookie.encrypted which returns an EncryptedCookieJar How to use it? cookies.encrypted[:discount] = 45 => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/ cookies.encrypted[:discount] => 45 --- .../lib/action_dispatch/middleware/cookies.rb | 48 ++++++++++++++++++++++ actionpack/test/dispatch/cookies_test.rb | 15 +++++++ .../lib/active_support/message_encryptor.rb | 7 +++- activesupport/test/message_encryptor_test.rb | 2 +- 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 0ec7f24a14..7206306b94 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/hash/keys' require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/message_verifier' module ActionDispatch class Request < Rack::Request @@ -242,6 +243,22 @@ module ActionDispatch @signed ||= SignedCookieJar.new(self, @key_generator) 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. + # + # This jar requires that you set a suitable secret for the verification on your app's +config.secret_token_key+. + # + # Example: + # + # cookies.encrypted[:discount] = 45 + # # => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/ + # + # cookies.encrypted[:discount] # => 45 + def encrypted + @encrypted ||= EncryptedCookieJar.new(self, @key_generator) + end + def write(headers) @set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) } @delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) } @@ -341,6 +358,37 @@ module ActionDispatch end end + class EncryptedCookieJar < SignedCookieJar #:nodoc: + def initialize(parent_jar, key_generator) + @parent_jar = parent_jar + secret = key_generator.generate_key('encrypted cookie') + sign_secret = key_generator.generate_key('signed encrypted cookie') + @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) + ensure_secret_secure(secret) + end + + def [](name) + if encrypted_message = @parent_jar[name] + @encryptor.decrypt_and_verify(encrypted_message) + end + rescue ActiveSupport::MessageVerifier::InvalidSignature, + ActiveSupport::MessageVerifier::InvalidMessage + nil + end + + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + else + options = { :value => options } + end + options[:value] = @encryptor.encrypt_and_sign(options[:value]) + + raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE + @parent_jar[key] = options + end + end + def initialize(app) @app = app end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index fc0db8b91d..0b6f4cb03f 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -67,6 +67,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def set_encrypted_cookie + cookies.encrypted[:foo] = 'bar' + head :ok + end + def raise_data_overflow cookies.signed[:foo] = 'bye!' * 1024 head :ok @@ -298,6 +303,16 @@ class CookiesTest < ActionController::TestCase assert_equal 45, @controller.send(:cookies).signed[:user_id] end + def test_encrypted_cookie + get :set_encrypted_cookie + cookies = @controller.send :cookies + assert_not_equal 'bar', cookies[:foo] + assert_raises TypeError do + cookies.signed[:foo] + end + assert_equal 'bar', cookies.encrypted[:foo] + end + def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature get :set_signed_cookie assert_nil @controller.send(:cookies).signed[:non_existant_attribute] diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 580267708c..1588674afc 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -39,10 +39,13 @@ module ActiveSupport # * :cipher - Cipher to use. Can be any cipher returned by # OpenSSL::Cipher.ciphers. Default is 'aes-256-cbc'. # * :serializer - Object serializer to use. Default is +Marshal+. - def initialize(secret, options = {}) + def initialize(secret, *signature_key_or_options) + options = signature_key_or_options.extract_options! + sign_secret = signature_key_or_options.first @secret = secret + @sign_secret = sign_secret @cipher = options[:cipher] || 'aes-256-cbc' - @verifier = MessageVerifier.new(@secret, :serializer => NullSerializer) + @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer) @serializer = options[:serializer] || Marshal end diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index b544742300..d6c31396b6 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -56,7 +56,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase end def test_alternative_serialization_method - encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), :serializer => JSONSerializer.new) + encryptor = ActiveSupport::MessageEncryptor.new(SecureRandom.hex(64), SecureRandom.hex(64), :serializer => JSONSerializer.new) message = encryptor.encrypt_and_sign({ :foo => 123, 'bar' => Time.utc(2010) }) assert_equal encryptor.decrypt_and_verify(message), { "foo" => 123, "bar" => "2010-01-01T00:00:00Z" } end -- cgit v1.2.3 From fb0cea2b8cf61cde1aa4c640b56e896fbe308aa1 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 30 Oct 2012 18:12:23 -0200 Subject: Add encrypted cookie store --- actionpack/lib/action_dispatch.rb | 9 ++-- .../middleware/session/cookie_store.rb | 23 ++++++++-- .../config/initializers/session_store.rb.tt | 2 +- .../test/application/middleware/session_test.rb | 51 ++++++++++++++++++++++ railties/test/generators/app_generator_test.rb | 2 +- 5 files changed, 78 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 0ec355246e..ab1755acd5 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -81,10 +81,11 @@ module ActionDispatch end module Session - autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' - autoload :CookieStore, '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 :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/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index 3f28ea75ef..039846688e 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -57,8 +57,7 @@ module ActionDispatch def unpacked_cookie_data(env) env["action_dispatch.request.unsigned_session_cookie"] ||= begin stale_session_check! do - request = ActionDispatch::Request.new(env) - if data = request.cookie_jar.signed[@key] + if data = cookie_jar(env)[@key] data.stringify_keys! end data || {} @@ -72,8 +71,26 @@ module ActionDispatch end def set_cookie(env, session_id, cookie) + cookie_jar(env)[@key] = cookie + end + + def get_cookie + cookie_jar(env)[@key] + end + + 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.signed[@key] = cookie + request.cookie_jar.encrypted end 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 4a099a4ce2..df07de9922 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 :cookie_store, key: <%= "'_#{app_name}_session'" %> +<%= app_const %>.config.session_store :encrypted_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 5ce41caf61..b1a19d590c 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -128,5 +128,56 @@ module ApplicationTests get '/foo/read_cookie' # Cookie shouldn't be changed assert_equal '"1"', last_response.body end + + test "session using 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.session_store :encrypted_cookie_store, key: '_myapp_session' + config.action_dispatch.derive_signed_cookie_key = true + RUBY + + require "#{app_path}/config/environment" + + get '/foo/write_session' + 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 end end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 62e1f3531e..5ea31f2e0f 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -341,7 +341,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_new_hash_style run_generator [destination_root] assert_file "config/initializers/session_store.rb" do |file| - assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file) + assert_match(/config.session_store :encrypted_cookie_store, key: '_.+_session'/, file) end end -- cgit v1.2.3 From 5d23925f84f0241e28b3fbce740150136ba08254 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 2 Nov 2012 20:26:11 -0200 Subject: Use derived keys everywhere, http_authentication was missing it --- .../lib/action_controller/metal/http_authentication.rb | 5 ++--- .../test/controller/http_digest_authentication_test.rb | 4 +++- railties/lib/rails/application.rb | 5 +++-- railties/lib/rails/application/configuration.rb | 8 ++------ railties/test/abstract_unit.rb | 1 + railties/test/application/configuration_test.rb | 18 ------------------ railties/test/application/middleware/remote_ip_test.rb | 4 +++- 7 files changed, 14 insertions(+), 31 deletions(-) diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 6d46586367..1537b8b806 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -249,9 +249,8 @@ module ActionController end def secret_token(request) - secret = request.env["action_dispatch.secret_token"] - raise "You must set config.secret_token in your app's config" if secret.blank? - secret + key_generator = request.env["action_dispatch.key_generator"] + key_generator.generate_key('http authentication') end # Uses an MD5 digest based on time to generate a value to be used only once. diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index b11ad633bd..dd95fad6e1 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +# FIXME remove DummyKeyGenerator and this require in 4.1 +require 'active_support/key_generator' class HttpDigestAuthenticationTest < ActionController::TestCase class DummyDigestController < ActionController::Base @@ -41,7 +43,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase setup do # Used as secret in generating nonce to prevent tampering of timestamp @secret = "session_options_secret" - @request.env["action_dispatch.secret_token"] = @secret + @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new(@secret) end teardown do diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 741b03d80e..f9867721a2 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -120,7 +120,6 @@ 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, @@ -135,11 +134,13 @@ module Rails ActiveSupport::Deprecation.warn "You didn't set config.secret_token_key. " + "This should be used instead of the old deprecated config.secret_token. " + "Set config.secret_token_key instead of config.secret_token in config/initializers/secret_token.rb" + if config.secret_token.blank? + raise "You must set config.secret_token_key in your app's config" + end end 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/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index b01b97aa67..0faa62c86c 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -10,12 +10,12 @@ module Rails :cache_classes, :cache_store, :consider_all_requests_local, :console, :eager_load, :exceptions_app, :file_watcher, :filter_parameters, :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags, - :railties_order, :relative_url_root, :secret_token_key, + :railties_order, :relative_url_root, :secret_token, :secret_token_key, :serve_static_assets, :ssl_options, :static_cache_control, :session_options, :time_zone, :reload_classes_only_on_change, :queue, :queue_consumer, :beginning_of_week - attr_writer :secret_token, :log_level + attr_writer :log_level attr_reader :encoding def initialize(*) @@ -146,10 +146,6 @@ module Rails def whiny_nils=(*) ActiveSupport::Deprecation.warn "config.whiny_nils option is deprecated and no longer works" end - - def secret_token - @secret_token_key || @secret_token - end end end end diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index dfcf5aa27d..486cc64137 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -14,5 +14,6 @@ require 'rails/all' module TestApp class Application < Rails::Application config.root = File.dirname(__FILE__) + config.secret_token_key = 'b3c631c314c0bbca50c1b2843150fe33' end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index daf9dd3505..5d654e1be6 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -225,23 +225,6 @@ module ApplicationTests assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path end - test "config.secret_token_key is sent in env" do - make_basic_app do |app| - app.config.secret_token_key = 'b3c631c314c0bbca50c1b2843150fe33' - app.config.session_store :disabled - end - - class ::OmgController < ActionController::Base - def index - cookies.signed[:some_key] = "some_value" - render text: env["action_dispatch.secret_token"] - end - end - - get "/" - assert_equal 'b3c631c314c0bbca50c1b2843150fe33', last_response.body - end - test "Use key_generator when secret_token_key is set" do make_basic_app do |app| app.config.secret_token_key = 'b3c631c314c0bbca50c1b2843150fe33' @@ -588,7 +571,6 @@ module ApplicationTests assert_respond_to app, :env_config assert_equal app.env_config['action_dispatch.parameter_filter'], app.config.filter_parameters - assert_equal app.env_config['action_dispatch.secret_token'], app.config.secret_token assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions assert_equal app.env_config['action_dispatch.logger'], Rails.logger assert_equal app.env_config['action_dispatch.backtrace_cleaner'], Rails.backtrace_cleaner diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb index 9d97bae9ae..fde13eeb94 100644 --- a/railties/test/application/middleware/remote_ip_test.rb +++ b/railties/test/application/middleware/remote_ip_test.rb @@ -1,4 +1,6 @@ require 'isolation/abstract_unit' +# FIXME remove DummyKeyGenerator and this require in 4.1 +require 'active_support/key_generator' module ApplicationTests class RemoteIpTest < ActiveSupport::TestCase @@ -8,7 +10,7 @@ module ApplicationTests remote_ip = nil env = Rack::MockRequest.env_for("/").merge(env).merge!( 'action_dispatch.show_exceptions' => false, - 'action_dispatch.secret_token' => 'b3c631c314c0bbca50c1b2843150fe33' + 'action_dispatch.key_generator' => ActiveSupport::DummyKeyGenerator.new('b3c631c314c0bbca50c1b2843150fe33') ) endpoint = Proc.new do |e| -- cgit v1.2.3 From 47da5744741f0af668d2f915e09003be35dcce66 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 1 Nov 2012 20:02:09 -0200 Subject: Allow users to change the default salt if they want, shouldn't be necessary --- .../action_controller/metal/http_authentication.rb | 5 ++-- .../lib/action_dispatch/middleware/cookies.rb | 33 ++++++++++++++-------- actionpack/lib/action_dispatch/railtie.rb | 4 +++ actionpack/test/dispatch/cookies_test.rb | 5 +++- railties/lib/rails/application.rb | 22 ++++++++++----- 5 files changed, 48 insertions(+), 21 deletions(-) diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 1537b8b806..d3b5bafee1 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -249,8 +249,9 @@ module ActionController end def secret_token(request) - key_generator = request.env["action_dispatch.key_generator"] - key_generator.generate_key('http authentication') + key_generator = request.env["action_dispatch.key_generator"] + http_auth_salt = request.env["action_dispatch.http_auth_salt"] + key_generator.generate_key(http_auth_salt) end # Uses an MD5 digest based on time to generate a value to be used only once. diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 7206306b94..daad64d041 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -82,6 +82,10 @@ module ActionDispatch class Cookies HTTP_HEADER = "Set-Cookie".freeze GENERATOR_KEY = "action_dispatch.key_generator".freeze + 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 + # Raised when storing more than 4K of session data. CookieOverflow = Class.new StandardError @@ -106,21 +110,25 @@ module ActionDispatch def self.build(request) env = request.env 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] } host = request.host secure = request.ssl? - new(key_generator, host, secure).tap do |hash| + new(key_generator, host, secure, options).tap do |hash| hash.update(request.cookies) end end - def initialize(key_generator, host = nil, secure = false) + def initialize(key_generator, host = nil, secure = false, options = {}) @key_generator = key_generator @set_cookies = {} @delete_cookies = {} @host = host @secure = secure + @options = options @cookies = {} end @@ -223,7 +231,7 @@ module ActionDispatch # cookies.permanent.signed[:remember_me] = current_user.id # # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT def permanent - @permanent ||= PermanentCookieJar.new(self, @key_generator) + @permanent ||= PermanentCookieJar.new(self, @key_generator, @options) end # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from @@ -240,7 +248,7 @@ module ActionDispatch # # cookies.signed[:discount] # => 45 def signed - @signed ||= SignedCookieJar.new(self, @key_generator) + @signed ||= SignedCookieJar.new(self, @key_generator, @options) end # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read. @@ -256,7 +264,7 @@ module ActionDispatch # # cookies.encrypted[:discount] # => 45 def encrypted - @encrypted ||= EncryptedCookieJar.new(self, @key_generator) + @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options) end def write(headers) @@ -280,9 +288,10 @@ module ActionDispatch end class PermanentCookieJar < CookieJar #:nodoc: - def initialize(parent_jar, key_generator) + def initialize(parent_jar, key_generator, options = {}) @parent_jar = parent_jar @key_generator = key_generator + @options = options end def []=(key, options) @@ -305,9 +314,10 @@ module ActionDispatch MAX_COOKIE_SIZE = 4096 # Cookies can typically store 4096 bytes. SECRET_MIN_LENGTH = 30 # Characters - def initialize(parent_jar, key_generator) + def initialize(parent_jar, key_generator, options = {}) @parent_jar = parent_jar - secret = key_generator.generate_key('signed cookie') + @options = options + secret = key_generator.generate_key(@options[:signed_cookie_salt]) ensure_secret_secure(secret) @verifier = ActiveSupport::MessageVerifier.new(secret) end @@ -359,10 +369,11 @@ module ActionDispatch end class EncryptedCookieJar < SignedCookieJar #:nodoc: - def initialize(parent_jar, key_generator) + def initialize(parent_jar, key_generator, options = {}) @parent_jar = parent_jar - secret = key_generator.generate_key('encrypted cookie') - sign_secret = key_generator.generate_key('signed encrypted cookie') + @options = options + secret = key_generator.generate_key(@options[:encrypted_cookie_salt]) + sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt]) @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) ensure_secret_secure(secret) end diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 284dd180db..98c87d9b2d 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -13,6 +13,10 @@ module ActionDispatch config.action_dispatch.rescue_responses = { } config.action_dispatch.default_charset = nil config.action_dispatch.rack_cache = false + config.action_dispatch.http_auth_salt = 'http authentication' + config.action_dispatch.signed_cookie_salt = 'signed cookie' + config.action_dispatch.encrypted_cookie_salt = 'encrypted cookie' + config.action_dispatch.encrypted_signed_cookie_salt = 'signed encrypted cookie' config.action_dispatch.default_headers = { 'X-Frame-Options' => 'SAMEORIGIN', diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 0b6f4cb03f..ffa91d63c4 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -153,7 +153,10 @@ class CookiesTest < ActionController::TestCase def setup super - @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") + @request.env["action_dispatch.key_generator"] = ActiveSupport::KeyGenerator.new("b3c631c314c0bbca50c1b2843150fe33") + @request.env["action_dispatch.signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.encrypted_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.encrypted_signed_cookie_salt"] = "b3c631c314c0bbca50c1b2843150fe33" @request.host = "www.nextangle.com" end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index f9867721a2..3ec29e1dd6 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -119,12 +119,16 @@ module Rails # will be used by middlewares and engines to configure themselves. # Currently stores: # - # * "action_dispatch.parameter_filter" => config.filter_parameters, - # * "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions, - # * "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local, - # * "action_dispatch.logger" => Rails.logger, - # * "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner - # * "action_dispatch.key_generator" => key_generator + # * "action_dispatch.parameter_filter" => config.filter_parameters + # * "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions + # * "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local + # * "action_dispatch.logger" => Rails.logger + # * "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner + # * "action_dispatch.key_generator" => key_generator + # * "action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt + # * "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt + # * "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt + # * "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt # # These parameters will be used by middlewares and engines to configure themselves # @@ -145,7 +149,11 @@ module Rails "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local, "action_dispatch.logger" => Rails.logger, "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner, - "action_dispatch.key_generator" => key_generator + "action_dispatch.key_generator" => key_generator, + "action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt, + "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt, + "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt, + "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt }) end end -- cgit v1.2.3 From 851e8fe897633f095a0f39a91f8bc75eee7a76aa Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 1 Nov 2012 20:23:21 -0200 Subject: Cache generated keys per KeyGenerator instance using salt + key_size --- activesupport/lib/active_support/key_generator.rb | 14 ++++++++++++++ railties/lib/rails/application.rb | 13 ++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 8b49ad8414..a8a4c17fd6 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -1,3 +1,4 @@ +require 'mutex_m' require 'openssl' module ActiveSupport @@ -21,6 +22,19 @@ module ActiveSupport end end + class CachingKeyGenerator + def initialize(key_generator) + @key_generator = key_generator + @cache_keys = {}.extend(Mutex_m) + end + + def generate_key(salt, key_size=64) + @cache_keys.synchronize do + @cache_keys["#{salt}#{key_size}"] ||= @key_generator.generate_key(salt, key_size) + end + end + end + class DummyKeyGenerator def initialize(secret) @secret = secret diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 3ec29e1dd6..f484e1737c 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -108,11 +108,14 @@ module Rails def key_generator # number of iterations selected based on consultation with the google security # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220 - @key_generator ||= if config.secret_token_key - ActiveSupport::KeyGenerator.new(config.secret_token_key, iterations: 1000) - else - ActiveSupport::DummyKeyGenerator.new(config.secret_token) - end + @caching_key_generator ||= begin + if config.secret_token_key + key_generator = ActiveSupport::KeyGenerator.new(config.secret_token_key, iterations: 1000) + ActiveSupport::CachingKeyGenerator.new(key_generator) + else + ActiveSupport::DummyKeyGenerator.new(config.secret_token) + end + end end # Stores some of the Rails initial environment parameters which -- cgit v1.2.3 From c2a7956eb7fbc099ea38d21601d215ab3def27fb Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 2 Nov 2012 11:03:18 -0200 Subject: Move ensure_secret_secure to DummyKeyGenerator --- .../lib/action_dispatch/middleware/cookies.rb | 24 ---------------------- .../controller/http_digest_authentication_test.rb | 2 +- activesupport/lib/active_support/key_generator.rb | 24 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index daad64d041..69da24f05b 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -312,13 +312,11 @@ module ActionDispatch class SignedCookieJar < CookieJar #:nodoc: MAX_COOKIE_SIZE = 4096 # Cookies can typically store 4096 bytes. - SECRET_MIN_LENGTH = 30 # Characters def initialize(parent_jar, key_generator, options = {}) @parent_jar = parent_jar @options = options secret = key_generator.generate_key(@options[:signed_cookie_salt]) - ensure_secret_secure(secret) @verifier = ActiveSupport::MessageVerifier.new(secret) end @@ -345,27 +343,6 @@ module ActionDispatch def method_missing(method, *arguments, &block) @parent_jar.send(method, *arguments, &block) end - - protected - - # To prevent users from using something insecure like "Password" we make sure that the - # secret they've provided is at least 30 characters in length. - def ensure_secret_secure(secret) - if secret.blank? - raise ArgumentError, "A secret is required to generate an " + - "integrity hash for cookie session data. Use " + - "config.secret_token_key = \"some secret phrase of at " + - "least #{SECRET_MIN_LENGTH} characters\"" + - "in config/initializers/secret_token.rb" - end - - if secret.length < SECRET_MIN_LENGTH - raise ArgumentError, "Secret should be something secure, " + - "like \"#{SecureRandom.hex(16)}\". The value you " + - "provided, \"#{secret}\", is shorter than the minimum length " + - "of #{SECRET_MIN_LENGTH} characters" - end - end end class EncryptedCookieJar < SignedCookieJar #:nodoc: @@ -375,7 +352,6 @@ module ActionDispatch secret = key_generator.generate_key(@options[:encrypted_cookie_salt]) sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt]) @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret) - ensure_secret_secure(secret) end def [](name) diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index dd95fad6e1..c4a94264c3 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -42,7 +42,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase setup do # Used as secret in generating nonce to prevent tampering of timestamp - @secret = "session_options_secret" + @secret = "4fb45da9e4ab4ddeb7580d6a35503d99" @request.env["action_dispatch.key_generator"] = ActiveSupport::DummyKeyGenerator.new(@secret) end diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index a8a4c17fd6..2b5a6fa0ba 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -36,12 +36,36 @@ module ActiveSupport end class DummyKeyGenerator + SECRET_MIN_LENGTH = 30 # Characters + def initialize(secret) + ensure_secret_secure(secret) @secret = secret end def generate_key(salt) @secret end + + private + + # To prevent users from using something insecure like "Password" we make sure that the + # secret they've provided is at least 30 characters in length. + def ensure_secret_secure(secret) + if secret.blank? + raise ArgumentError, "A secret is required to generate an " + + "integrity hash for cookie session data. Use " + + "config.secret_token_key = \"some secret phrase of at " + + "least #{SECRET_MIN_LENGTH} characters\"" + + "in config/initializers/secret_token.rb" + end + + if secret.length < SECRET_MIN_LENGTH + raise ArgumentError, "Secret should be something secure, " + + "like \"#{SecureRandom.hex(16)}\". The value you " + + "provided, \"#{secret}\", is shorter than the minimum length " + + "of #{SECRET_MIN_LENGTH} characters" + end + end end end -- cgit v1.2.3 From 4faa0418453055bc81456685d418d486252cc379 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 2 Nov 2012 20:27:51 -0200 Subject: Rename secret_token_key to secret_key_base --- actionpack/lib/action_dispatch/middleware/cookies.rb | 6 +++--- activesupport/lib/active_support/key_generator.rb | 2 +- .../code/getting_started/config/initializers/secret_token.rb | 2 +- guides/source/action_controller_overview.md | 2 +- guides/source/configuring.md | 2 +- railties/lib/rails/application.rb | 12 ++++++------ railties/lib/rails/application/configuration.rb | 4 ++-- .../app/templates/config/initializers/secret_token.rb.tt | 4 ++-- railties/test/abstract_unit.rb | 2 +- railties/test/application/configuration_test.rb | 4 ++-- railties/test/application/url_generation_test.rb | 2 +- railties/test/isolation/abstract_unit.rb | 4 ++-- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 69da24f05b..1090473797 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -28,7 +28,7 @@ module ActionDispatch # cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now } # # # Sets a signed cookie, which prevents users from tampering with its value. - # # The cookie is signed by your app's config.secret_token_key value. + # # The cookie is signed by your app's config.secret_key_base value. # # It can be read using the signed method cookies.signed[:key] # cookies.signed[:user_id] = current_user.id # @@ -239,7 +239,7 @@ module ActionDispatch # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will # be raised. # - # This jar requires that you set a suitable secret for the verification on your app's +config.secret_token_key+. + # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. # # Example: # @@ -255,7 +255,7 @@ module ActionDispatch # If the cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception # will be raised. # - # This jar requires that you set a suitable secret for the verification on your app's +config.secret_token_key+. + # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. # # Example: # diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 2b5a6fa0ba..2c8fccec7d 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -55,7 +55,7 @@ module ActiveSupport if secret.blank? raise ArgumentError, "A secret is required to generate an " + "integrity hash for cookie session data. Use " + - "config.secret_token_key = \"some secret phrase of at " + + "config.secret_key_base = \"some secret phrase of at " + "least #{SECRET_MIN_LENGTH} characters\"" + "in config/initializers/secret_token.rb" end diff --git a/guides/code/getting_started/config/initializers/secret_token.rb b/guides/code/getting_started/config/initializers/secret_token.rb index 8d52c11b69..969ecaad65 100644 --- a/guides/code/getting_started/config/initializers/secret_token.rb +++ b/guides/code/getting_started/config/initializers/secret_token.rb @@ -6,4 +6,4 @@ # no regular words or you'll be exposed to dictionary attacks. # Make sure your secret key is kept private # if you're sharing your code publicly. -Blog::Application.config.secret_token_key = '685a9bf865b728c6549a191c90851c1b5ec41ecb60b9e94ad79dd3f824749798aa7b5e94431901960bee57809db0947b481570f7f13376b7ca190fa28099c459' +Blog::Application.config.secret_key_base = '685a9bf865b728c6549a191c90851c1b5ec41ecb60b9e94ad79dd3f824749798aa7b5e94431901960bee57809db0947b481570f7f13376b7ca190fa28099c459' diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index d5d74ca3f5..5be2991c4a 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -219,7 +219,7 @@ Rails sets up (for the CookieStore) a secret key used for signing the session da # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. -YourApp::Application.config.secret_token_key = '49d3f3de9ed86c74b94ad6bd0...' +YourApp::Application.config.secret_key_base = '49d3f3de9ed86c74b94ad6bd0...' ``` NOTE: Changing the secret when using the `CookieStore` will invalidate all existing sessions. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index cadc09ae33..b5bedfb1b7 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -113,7 +113,7 @@ These configuration methods are to be called on a `Rails::Railtie` object, such * `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If `config.cache_classes` is true, this option is ignored. -* `config.secret_token_key` used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_token_key` initialized to a random key in `config/initializers/secret_token.rb`. +* `config.secret_key_base` used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_key_base` initialized to a random key in `config/initializers/secret_token.rb`. * `config.serve_static_assets` configures Rails itself to serve static assets. Defaults to true, but in the production environment is turned off as the server software (e.g. Nginx or Apache) used to run the application should serve static assets instead. Unlike the default setting set this to true when running (absolutely not recommended!) or testing your app in production mode using WEBrick. Otherwise you won´t be able use page caching and requests for files that exist regularly under the public directory will anyway hit your Rails app. diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index f484e1737c..b7844bbfbe 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -109,8 +109,8 @@ module Rails # number of iterations selected based on consultation with the google security # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220 @caching_key_generator ||= begin - if config.secret_token_key - key_generator = ActiveSupport::KeyGenerator.new(config.secret_token_key, iterations: 1000) + if config.secret_key_base + key_generator = ActiveSupport::KeyGenerator.new(config.secret_key_base, iterations: 1000) ActiveSupport::CachingKeyGenerator.new(key_generator) else ActiveSupport::DummyKeyGenerator.new(config.secret_token) @@ -137,12 +137,12 @@ module Rails # def env_config @env_config ||= begin - if config.secret_token_key.nil? - ActiveSupport::Deprecation.warn "You didn't set config.secret_token_key. " + + if config.secret_key_base.nil? + ActiveSupport::Deprecation.warn "You didn't set config.secret_key_base. " + "This should be used instead of the old deprecated config.secret_token. " + - "Set config.secret_token_key instead of config.secret_token in config/initializers/secret_token.rb" + "Set config.secret_key_base instead of config.secret_token in config/initializers/secret_token.rb" if config.secret_token.blank? - raise "You must set config.secret_token_key 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/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 0faa62c86c..f97e66985c 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -10,7 +10,7 @@ module Rails :cache_classes, :cache_store, :consider_all_requests_local, :console, :eager_load, :exceptions_app, :file_watcher, :filter_parameters, :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags, - :railties_order, :relative_url_root, :secret_token, :secret_token_key, + :railties_order, :relative_url_root, :secret_key_base, :secret_token, :serve_static_assets, :ssl_options, :static_cache_control, :session_options, :time_zone, :reload_classes_only_on_change, :queue, :queue_consumer, :beginning_of_week @@ -47,7 +47,7 @@ module Rails @queue_consumer = nil @eager_load = nil @secret_token = nil - @secret_token_key = nil + @secret_key_base = nil @assets = ActiveSupport::OrderedOptions.new @assets.enabled = false diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt index d96185ae2a..e5caab3672 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt @@ -7,6 +7,6 @@ # no regular words or you'll be exposed to dictionary attacks. # You can use `rake secret` to generate a secure secret key. -# Make sure your secret_token_key is kept private +# Make sure your secret_key_base is kept private # if you're sharing your code publicly. -<%= app_const %>.config.secret_token_key = '<%= app_secret %>' +<%= app_const %>.config.secret_key_base = '<%= app_secret %>' diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index 486cc64137..2ea1d2aff4 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -14,6 +14,6 @@ require 'rails/all' module TestApp class Application < Rails::Application config.root = File.dirname(__FILE__) - config.secret_token_key = 'b3c631c314c0bbca50c1b2843150fe33' + config.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33' end end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 5d654e1be6..b9d18f4582 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -225,9 +225,9 @@ module ApplicationTests assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path end - test "Use key_generator when secret_token_key is set" do + test "Use key_generator when secret_key_base is set" do make_basic_app do |app| - app.config.secret_token_key = 'b3c631c314c0bbca50c1b2843150fe33' + app.config.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33' app.config.session_store :disabled end diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb index fb83659b0c..0905757442 100644 --- a/railties/test/application/url_generation_test.rb +++ b/railties/test/application/url_generation_test.rb @@ -14,7 +14,7 @@ module ApplicationTests require "action_controller/railtie" class MyApp < Rails::Application - config.secret_token_key = "3b7cd727ee24e8444053437c36cc66c4" + config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4" config.session_store :cookie_store, key: "_myapp_session" config.active_support.deprecation = :log config.eager_load = false diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 2c92f2ded5..0cb65f8e0d 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -119,7 +119,7 @@ module TestHelpers add_to_config <<-RUBY config.eager_load = false - config.secret_token_key = "3b7cd727ee24e8444053437c36cc66c4" + config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4" config.session_store :cookie_store, key: "_myapp_session" config.active_support.deprecation = :log config.action_controller.allow_forgery_protection = false @@ -138,7 +138,7 @@ module TestHelpers app = Class.new(Rails::Application) app.config.eager_load = false - app.config.secret_token_key = "3b7cd727ee24e8444053437c36cc66c4" + app.config.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4" app.config.session_store :cookie_store, key: "_myapp_session" app.config.active_support.deprecation = :log -- cgit v1.2.3 From d63783983f8c03d5c624938081615579dcc753f7 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 2 Nov 2012 00:43:24 -0200 Subject: Disallow ability to use EncryptedCookieJar with DummyKeyGenerator Developers must set config.secret_key_base in config/initializers/secret_token.rb --- actionpack/lib/action_dispatch/middleware/cookies.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 1090473797..7936dcb515 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -347,6 +347,11 @@ module ActionDispatch class EncryptedCookieJar < SignedCookieJar #: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" + end + @parent_jar = parent_jar @options = options secret = key_generator.generate_key(@options[:encrypted_cookie_salt]) -- cgit v1.2.3