diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index adad743d38..845df500d8 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -89,13 +89,11 @@ module ActionDispatch # cookies[:login] = { value: "XJ-122", expires: Time.utc(2020, 10, 15, 5) } # # # Sets a signed cookie, which prevents users from tampering with its value. - # # The cookie is signed by your app's `secrets.secret_key_base` value. # # It can be read using the signed method `cookies.signed[:name]` # cookies.signed[:user_id] = current_user.id # # # Sets an encrypted cookie value before sending it to the client which # # prevent users from reading and tampering with its value. - # # The cookie is signed by your app's `secrets.secret_key_base` value. # # It can be read using the encrypted method `cookies.encrypted[:name]` # cookies.encrypted[:discount] = 45 # @@ -191,10 +189,10 @@ module ActionDispatch # 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 +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set, + # If +secret_key_base+ and +secrets.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 +secrets.secret_key_base+. + # This jar requires that you set a suitable secret for the verification on your app's +secret_key_base+. # # Example: # @@ -214,13 +212,13 @@ module ActionDispatch # 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 +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set, + # If +secret_key_base+ and +secrets.secret_token+ (deprecated) are both set, # legacy cookies signed with the old key generator will be transparently upgraded. # # If +config.action_dispatch.encrypted_cookie_salt+ and +config.action_dispatch.encrypted_signed_cookie_salt+ # are both set, legacy cookies encrypted with HMAC AES-256-CBC will be transparently upgraded. # - # This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+. + # This jar requires that you set a suitable secret for the verification on your app's +secret_key_base+. # # Example: # @@ -591,7 +589,7 @@ module ActionDispatch end # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if - # secrets.secret_token and secrets.secret_key_base are both set. It reads + # secrets.secret_token and secret_key_base are both set. It reads # legacy cookies signed with the old dummy key generator and signs and # re-saves them using the new key generator to provide a smooth upgrade path. class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc: @@ -605,7 +603,7 @@ module ActionDispatch super if ActiveSupport::LegacyKeyGenerator === key_generator - raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " \ + raise "You didn't set secret_key_base, which is required for this cookie jar. " \ "Read the upgrade documentation to learn more about this new config option." end @@ -631,7 +629,7 @@ module ActionDispatch end # UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore - # instead of EncryptedCookieJar if secrets.secret_token and secrets.secret_key_base + # instead of EncryptedCookieJar if secrets.secret_token and 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: diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index f594b6f491..b0514a96d8 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -21,14 +21,10 @@ module ActionDispatch # knowing your app's secret key, but can easily read their +user_id+. This # was the default for Rails 3 apps. # - # If you have secret_key_base set, your cookies will be encrypted. This + # Your cookies will be encrypted using your apps secret_key_base. This # goes a step further than signed cookies in that encrypted cookies cannot # be altered or read by users. This is the default starting in Rails 4. # - # If you have both secret_token and secret_key_base set, your cookies will - # be encrypted, and signed cookies generated by Rails 3 will be - # transparently read and encrypted to provide a smooth upgrade path. - # # Configure your session store in config/initializers/session_store.rb: # # Rails.application.config.session_store :cookie_store, key: '_your_app_session' @@ -40,20 +36,10 @@ module ActionDispatch # If your application was not updated to Rails 5.2 defaults, the secret_key_base # will be found in the old config/secrets.yml file. # - # If you are upgrading an existing Rails 3 app, you should leave your - # existing secret_token in place and simply add the new secret_key_base. - # Note that you should wait to set secret_key_base until you have 100% of - # your userbase on Rails 4 and are reasonably sure you will not need to - # rollback to Rails 3. This is because cookies signed based on the new - # secret_key_base in Rails 4 are not backwards compatible with Rails 3. - # 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. Additionally, - # you should 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. - # - # Note that changing the secret key will invalidate all existing sessions! + # Note that changing your secret_key_base will invalidate all existing session. + # Additionally, you should 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 changing it. # # Because CookieStore extends Rack::Session::Abstract::Persisted, many of the # options described there can be used to customize the session cookie that diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb index 31de37b400..78f7d7ca8d 100644 --- a/activesupport/lib/active_support/key_generator.rb +++ b/activesupport/lib/active_support/key_generator.rb @@ -59,7 +59,7 @@ module ActiveSupport if secret.blank? raise ArgumentError, "A secret is required to generate an integrity hash " \ "for cookie session data. Set a secret_key_base of at least " \ - "#{SECRET_MIN_LENGTH} characters in config/secrets.yml." + "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`." end if secret.length < SECRET_MIN_LENGTH diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index b3b5f19b61..2c3f74c3e1 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -397,34 +397,18 @@ You can also pass a `:domain` key and specify the domain name for the cookie: Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com" ``` -Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in `config/secrets.yml` +Rails sets up (for the CookieStore) a secret key used for signing the session data in `config/credentials.yml.enc`. This can be changed with `bin/rails credentials:edit`. ```ruby -# Be sure to restart your server when you modify this file. +# amazon: +# access_key_id: 123 +# secret_access_key: 345 -# Your secret key is used for verifying the integrity of signed cookies. -# 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. -# You can use `rails secret` to generate a secure secret key. - -# Make sure the secrets in this file are kept private -# if you're sharing your code publicly. - -development: - secret_key_base: a75d... - -test: - secret_key_base: 492f... - -# Do not keep production secrets in the repository, -# instead read values from the environment. -production: - secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> +# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies. +secret_key_base: 492f... ``` -NOTE: Changing the secret when using the `CookieStore` will invalidate all existing sessions. +NOTE: Changing the secret_key_base when using the `CookieStore` will invalidate all existing sessions. ### Accessing the Session diff --git a/guides/source/configuring.md b/guides/source/configuring.md index d4e1d7b5dd..1c720ad82f 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -138,7 +138,7 @@ defaults to `:debug` for all environments. The available log levels are: `:debug * `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. -* `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`. +* `secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get a random generated key in test and development environments, other environments should set one in `config/credentials.yml.enc`. * `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to `true`, but in the production environment it is set to `false` because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to `true.` Otherwise, you won't be able to use page caching and request for files that exist under the public directory. diff --git a/guides/source/security.md b/guides/source/security.md index 882daa9806..a74de22ac0 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -98,7 +98,7 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves In Rails 4, encrypted cookies through AES in CBC mode with HMAC using SHA1 for verification was introduced. This prevents the user from accessing and tampering the content of the cookie. Thus the session becomes a more secure place to store -data. The encryption is performed using a server-side `secrets.secret_key_base`. +data. The encryption is performed using a server-side `secret_key_base`. Two salts are used when deriving keys for encryption and verification. These salts are set via the `config.action_dispatch.encrypted_cookie_salt` and `config.action_dispatch.encrypted_signed_cookie_salt` configuration values. @@ -111,18 +111,9 @@ Encrypted cookies are automatically upgraded if the _Do not use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters! Instead use `rails secret` to generate secret keys!_ -Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`, e.g.: +In test and development applications get a `secret_key_base` derived from the app name. Other environments must use a random key present in `config/credentials.yml.enc`, shown here in its decrypted state: - development: - secret_key_base: a75d... - - test: - secret_key_base: 492f... - - production: - secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> - -Older versions of Rails use CookieStore, which uses `secret_token` instead of `secret_key_base` that is used by EncryptedCookieStore. Read the upgrade documentation for more information. + secret_key_base: 492f... If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. @@ -1032,27 +1023,33 @@ Environmental Security It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/secrets.yml`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. -### Custom secrets +### Custom credentials -Rails generates a `config/secrets.yml`. By default, this file contains the -application's `secret_key_base`, but it could also be used to store other -secrets such as access keys for external APIs. +Rails generates a `config/credentials.yml.enc` to store third-party credentials +within the repo. This is only viable because Rails encrypts the file with a master +key that's generated into a version control ignored `config/master.key` — Rails +will also look for that key in `ENV["RAILS_MASTER_KEY"]`. Rails also requires the +the key to boot in production, so the credentials can be read. -The secrets added to this file are accessible via `Rails.application.secrets`. -For example, with the following `config/secrets.yml`: +To edit stored credentials use `bin/rails credentials:edit`. - development: - secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 - some_api_key: SOMEKEY +By default, this file contains the application's +`secret_key_base`, but it could also be used to store other credentials such as +access keys for external APIs. -`Rails.application.secrets.some_api_key` returns `SOMEKEY` in the development -environment. +The credentials added to this file are accessible via `Rails.application.credentials`. +For example, with the following decrypted `config/credentails.yml.enc`: + + secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 + some_api_key: SOMEKEY + +`Rails.application.credentails.some_api_key` returns `SOMEKEY` in any environment. If you want an exception to be raised when some key is blank, use the bang version: ```ruby -Rails.application.secrets.some_api_key! # => raises KeyError: key not found: :some_api_key +Rails.application.credentails.some_api_key! # => raises KeyError: key not found: :some_api_key ``` Additional Resources diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 6ce8b0b2d9..abfec90b6d 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -403,12 +403,12 @@ module Rails end # The secret_key_base is used as the input secret to the application's key generator, which in turn - # is used to create all the MessageVerfiers, including the one that signs and encrypts cookies. + # is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies. # # In test and development, this is simply derived as a MD5 hash of the application's name. # # In all other environments, we look for it first in ENV["SECRET_KEY_BASE"], - # then credentials[:secret_key_base], and finally secrets.secret_key_base. For most applications, + # then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications, # the correct place to store it is in the encrypted credentials file. def secret_key_base if Rails.env.test? || Rails.env.development? diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE index 5bd9f940fd..85877c71b7 100644 --- a/railties/lib/rails/commands/credentials/USAGE +++ b/railties/lib/rails/commands/credentials/USAGE @@ -10,7 +10,7 @@ to get everything working as the keys are shipped with the code. === Setup Applications after Rails 5.2 automatically have a basic credentials file generated -that just contains the secret_key_base used by the MessageVerifiers, like the one +that just contains the secret_key_base used by MessageVerifiers/MessageEncryptors, like the ones signing and encrypting cookies. For applications created prior to Rails 5.2, we'll automatically generate a new