diff options
author | Kasper Timm Hansen <kaspth@gmail.com> | 2017-09-24 21:41:16 +0200 |
---|---|---|
committer | Kasper Timm Hansen <kaspth@gmail.com> | 2017-09-24 21:41:16 +0200 |
commit | 38308e6d1353eda587d676ac40ce489c638fb0c3 (patch) | |
tree | d4e827efdaaf6d50b75cd7df9881e65f8c9ec978 | |
parent | 9d79d77813c3aca010a5b40cacbd6e68f42ce618 (diff) | |
download | rails-38308e6d1353eda587d676ac40ce489c638fb0c3.tar.gz rails-38308e6d1353eda587d676ac40ce489c638fb0c3.tar.bz2 rails-38308e6d1353eda587d676ac40ce489c638fb0c3.zip |
[ci skip] Attempt a new explanation for rotations.
It's become clear to me that the use case is still a bit muddy
and the upgrade path is going to be tough for people to figure
out.
This attempts at understanding it better through documentation,
but still needs follow up work.
[ Michael Coyne & Kasper Timm Hansen ]
-rw-r--r-- | activesupport/lib/active_support/message_encryptor.rb | 46 | ||||
-rw-r--r-- | activesupport/lib/active_support/message_verifier.rb | 45 | ||||
-rw-r--r-- | guides/source/configuring.md | 6 | ||||
-rw-r--r-- | guides/source/security.md | 44 |
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 |