aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb46
-rw-r--r--activesupport/lib/active_support/message_verifier.rb45
-rw-r--r--guides/source/configuring.md6
-rw-r--r--guides/source/security.md44
4 files changed, 60 insertions, 81 deletions
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 003fb4c354..8a1918039c 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -57,31 +57,27 @@ module ActiveSupport
#
# === Rotating keys
#
- # This class also defines a +rotate+ method which can be used to rotate out
- # encryption keys no longer in use.
- #
- # This method is called with an options hash where a +:cipher+ option and
- # either a +:raw_key+ or +:secret+ option must be defined. If +:raw_key+ is
- # defined, it is used directly for the underlying encryption function. If
- # the +:secret+ option is defined, a +:salt+ option must also be defined and
- # a +KeyGenerator+ instance will be used to derive a key using +:salt+. When
- # +:secret+ is used, a +:key_generator+ option may also be defined allowing
- # for custom +KeyGenerator+ instances. If CBC encryption is used a
- # `:raw_signed_key` or a `:signed_salt` option must also be defined. A
- # +:digest+ may also be defined when using CBC encryption. This method can be
- # called multiple times and new encryptor instances will be added to the
- # rotation stack on each call.
- #
- # # Specifying the key used for encryption
- # crypt.rotate raw_key: old_aead_key, cipher: "aes-256-gcm"
- # crypt.rotate raw_key: old_cbc_key, raw_signed_key: old_cbc_sign_key, cipher: "aes-256-cbc", digest: "SHA1"
- #
- # # Using a KeyGenerator instance with a secret and salt(s)
- # crypt.rotate secret: old_aead_secret, salt: old_aead_salt, cipher: "aes-256-gcm"
- # crypt.rotate secret: old_cbc_secret, salt: old_cbc_salt, signed_salt: old_cbc_signed_salt, cipher: "aes-256-cbc", digest: "SHA1"
- #
- # # Specifying the key generator instance
- # crypt.rotate key_generator: old_key_gen, salt: old_salt, cipher: "aes-256-gcm"
+ # MessageEncryptor also supports rotating out old configurations by falling
+ # back to a stack of encryptors. Call `rotate` to build and add an encryptor
+ # so `decrypt_and_verify` will also try the fallback.
+ #
+ # By default any rotated encryptors use the values of the primary
+ # encryptor unless specified otherwise.
+ #
+ # You'd give your encryptor the new defaults:
+ #
+ # crypt = ActiveSupport::MessageEncryptor.new(@secret, cipher: "aes-256-gcm")
+ #
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
+ # generated with the old values will then work until the rotation is removed.
+ #
+ # crypt.rotate old_secret # Fallback to an old secret instead of @secret.
+ # crypt.rotate cipher: "aes-256-cbc" # Fallback to an old cipher instead of aes-256-gcm.
+ #
+ # Though if both the secret and the cipher was changed at the same time,
+ # the above should be combined into:
+ #
+ # verifier.rotate old_secret, cipher: "aes-256-cbc"
class MessageEncryptor
prepend Messages::Rotator::Encryptor
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 0be13f6f03..f0b6503b96 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -77,30 +77,27 @@ module ActiveSupport
#
# === Rotating keys
#
- # This class also defines a +rotate+ method which can be used to rotate out
- # verification keys no longer in use.
- #
- # This method is called with an options hash where a +:digest+ option and
- # either a +:raw_key+ or +:secret+ option must be defined. If +:raw_key+ is
- # defined, it is used directly for the underlying HMAC function. If the
- # +:secret+ option is defined, a +:salt+ option must also be defined and a
- # +KeyGenerator+ instance will be used to derive a key using +:salt+. When
- # +:secret+ is used, a +:key_generator+ option may also be defined allowing
- # for custom +KeyGenerator+ instances. This method can be called multiple
- # times and new verifier instances will be added to the rotation stack on
- # each call.
- #
- # # Specifying the key used for verification
- # @verifier.rotate raw_key: older_key, digest: "SHA1"
- #
- # # Specify the digest
- # @verifier.rotate raw_key: old_key, digest: "SHA256"
- #
- # # Using a KeyGenerator instance with a secret and salt
- # @verifier.rotate secret: old_secret, salt: old_salt, digest: "SHA1"
- #
- # # Specifying the key generator instance
- # @verifier.rotate key_generator: old_key_gen, salt: old_salt, digest: "SHA256"
+ # MessageVerifier also supports rotating out old configurations by falling
+ # back to a stack of verifiers. Call `rotate` to build and add a verifier to
+ # so either `verified` or `verify` will also try verifying with the fallback.
+ #
+ # By default any rotated verifiers use the values of the primary
+ # verifier unless specified otherwise.
+ #
+ # You'd give your verifier the new defaults:
+ #
+ # verifier = ActiveSupport::MessageVerifier.new(@secret, digest: "SHA512", serializer: JSON)
+ #
+ # Then gradually rotate the old values out by adding them as fallbacks. Any message
+ # generated with the old values will then work until the rotation is removed.
+ #
+ # verifier.rotate old_secret # Fallback to an old secret instead of @secret.
+ # verifier.rotate digest: "SHA256" # Fallback to an old digest instead of SHA512.
+ # verifier.rotate serializer: Marshal # Fallback to an old serializer instead of JSON.
+ #
+ # Though the above would most likely be combined into one rotation:
+ #
+ # verifier.rotate old_secret, digest: "SHA256", serializer: Marshal
class MessageVerifier
prepend Messages::Rotator::Verifier
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 86c8364d83..0f87d73d6e 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -493,10 +493,8 @@ Defaults to `'signed cookie'`.
* `config.action_dispatch.signed_cookie_digest` sets the digest to be
used for signed cookies. This defaults to `"SHA1"`.
-* `config.action_dispatch.cookies_rotations` is set to an instance of
- [RotationConfiguration](http://api.rubyonrails.org/classes/ActiveSupport/RotationConfiguration.html).
- It provides an interface for rotating keys, salts, ciphers, and
- digests for encrypted and signed cookies.
+* `config.action_dispatch.cookies_rotations` allows rotating
+ secrets, ciphers, and digests for encrypted and signed cookies.
* `config.action_dispatch.perform_deep_munge` configures whether `deep_munge`
method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation)
diff --git a/guides/source/security.md b/guides/source/security.md
index 994978b88b..9e1dc518d2 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -161,43 +161,31 @@ It is also useful to rotate this value for other more benign reasons,
such as an employee leaving your organization or changing hosting
environments.
-Key rotations can be defined through
-`config.action_dispatch.cookies_rotations` which provides an interface for
-rotating signed and encrypted cookie keys, salts, digests, and ciphers.
-
-For example, suppose we want to rotate out an old `secret_key_base`, we
-can define a signed and encrypted key rotation as follows:
+For example to rotate out an old `secret_key_base`, we can define signed and
+encrypted rotations as follows:
```ruby
-config.action_dispatch.cookies_rotations.rotate :encrypted,
- cipher: "aes-256-gcm",
- secret: Rails.application.credentials.old_secret_key_base,
- salt: config.action_dispatch.authenticated_encrypted_cookie_salt
-
-config.action_dispatch.cookies_rotations.rotate :signed,
- digest: "SHA1",
- secret: Rails.application.credentials.old_secret_key_base,
- salt: config.action_dispatch.signed_cookie_salt
+Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
+ cookies.rotate :encrypted, secret: Rails.application.credentials.old_secret_key_base
+ cookies.rotate :signed, secret: Rails.application.credentials.old_secret_key_base
+end
```
-Multiple rotations are possible by calling `rotate` multiple times. For
-example, suppose we want to use SHA512 for signed cookies while rotating
-out SHA256 and SHA1 digests using the same `secret_key_base`:
+It's also possible to set up multiple rotations. For instance to use `SHA512`
+for signed cookies while rotating out SHA256 and SHA1 digests, we'd do:
```ruby
-config.action_dispatch.signed_cookie_digest = "SHA512"
+Rails.application.config.action_dispatch.signed_cookie_digest = "SHA512"
-config.action_dispatch.cookies_rotations.rotate :signed,
- digest: "SHA256",
- secret: Rails.application.credentials.secret_key_base,
- salt: config.action_dispatch.signed_cookie_salt
-
-config.action_dispatch.cookies_rotations.rotate :signed,
- digest: "SHA1",
- secret: Rails.application.credentials.secret_key_base,
- salt: config.action_dispatch.signed_cookie_salt
+Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
+ cookies.rotate :signed, digest: "SHA256"
+ cookies.rotate :signed, digest: "SHA1"
+end
```
+While you can setup as many rotations as you'd like it's not common to have many
+rotations going at any one time.
+
For more details on key rotation with encrypted and signed messages as
well as the various options the `rotate` method accepts, please refer to
the