aboutsummaryrefslogtreecommitdiffstats
path: root/guides
diff options
context:
space:
mode:
authorMichael Coyne <mikeycgto@gmail.com>2017-09-23 17:18:01 -0400
committerMichael Coyne <mikeycgto@gmail.com>2017-09-24 12:23:38 -0400
commit8b0af54bbe5ab8b598e980013dd53a50d819b636 (patch)
tree05c883f46d687c0483db2313185420804e13c4c7 /guides
parent39f8ca64cec8667b66628e970211b4d18abbc373 (diff)
downloadrails-8b0af54bbe5ab8b598e980013dd53a50d819b636.tar.gz
rails-8b0af54bbe5ab8b598e980013dd53a50d819b636.tar.bz2
rails-8b0af54bbe5ab8b598e980013dd53a50d819b636.zip
Add key rotation cookies middleware
Using the action_dispatch.cookies_rotations interface, key rotation is now possible with cookies. Thus the secret_key_base as well as salts, ciphers, and digests, can be rotated without expiring sessions.
Diffstat (limited to 'guides')
-rw-r--r--guides/source/configuring.md11
-rw-r--r--guides/source/security.md130
2 files changed, 122 insertions, 19 deletions
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 1c720ad82f..86c8364d83 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -487,6 +487,17 @@ Defaults to `'signed cookie'`.
authenticated encrypted cookie salt. Defaults to `'authenticated encrypted
cookie'`.
+* `config.action_dispatch.encrypted_cookie_cipher` sets the cipher to be
+ used for encrypted cookies. This defaults to `"aes-256-gcm"`.
+
+* `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.perform_deep_munge` configures whether `deep_munge`
method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation)
for more information. It defaults to `true`.
diff --git a/guides/source/security.md b/guides/source/security.md
index 0b2d8de0fb..b0b71cad7d 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -85,37 +85,129 @@ This will also be a good idea, if you modify the structure of an object and old
* _Critical data should not be stored in session_. If the user clears their cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data.
-### Session Storage
+### Encrypted Session Storage
NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is `ActionDispatch::Session::CookieStore`._
-Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session ID. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:
+The `CookieStore` saves the session hash directly in a cookie on the
+client-side. The server retrieves the session hash from the cookie and
+eliminates the need for a session ID. That will greatly increase the
+speed of the application, but it is a controversial storage option and
+you have to think about the security implications and storage
+limitations of it:
+
+* Cookies imply a strict size limit of 4kB. This is fine as you should
+ not store large amounts of data in a session anyway, as described
+ before. Storing the current user's database id in a session is common
+ practice.
+
+* Session cookies do not invalidate themselves and can be maliciously
+ reused. It may be a good idea to have your application invalidate old
+ session cookies using a stored timestamp.
+
+The `CookieStore` uses the
+[encrypted](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-encrypted)
+cookie jar to provide a secure, encrypted location to store session
+data. Cookie-based sessions thus provide both integrity as well as
+confidentiality to their contents. The encryption key, as well as the
+verification key used for
+[signed](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-signed)
+cookies, is derived from the `secret_key_base` configuration value.
+
+As of Rails 5.2 encrypted cookies and sessions are protected using AES
+GCM encryption. This form of encryption is a type of Authenticated
+Encryption and couples authentication and encryption in single step
+while also producing shorter ciphertexts as compared to other
+algorithms previously used. The key for cookies encrypted with AES GCM
+are derived using a salt value defined by the
+`config.action_dispatch.authenticated_encrypted_cookie_salt`
+configuration value.
+
+Prior to this version, encrypted cookies were secured using AES in CBC
+mode with HMAC using SHA1 for authentication. The keys for this type of
+encryption and for HMAC verification were derived via the salts defined
+by `config.action_dispatch.encrypted_cookie_salt` and
+`config.action_dispatch.encrypted_signed_cookie_salt` respectively.
+
+Prior to Rails version 4 in both versions 2 and 3, session cookies were
+protected using only HMAC verification. As such, these session cookies
+only provided integrity to their content because the actual session data
+was stored in plaintext encoded as base64. This is how `signed` cookies
+work in the current version of Rails. These kinds of cookies are still
+useful for protecting the integrity of certain client-stored data and
+information.
+
+__Do not use a trivial secret for the `secret_key_base`, i.e. a word
+from a dictionary, or one which is shorter than 30 characters! Instead
+use `rails secret` to generate secret keys!__
+
+It is also important to use different salt values for encrypted and
+signed cookies. Using the same value for different salt configuration
+values may lead to the same derived key being used for different
+security features which in turn may weaken the strength of the key.
-* Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _Storing the current user's database id in a session is usually ok_.
+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:
-* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret (`secrets.secret_token`) and inserted into the end of the cookie.
+ secret_key_base: 492f...
-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 `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.
+If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret.
-Rails 5.2 uses AES-GCM for the encryption which couples authentication
-and encryption in one faster step and produces shorter ciphertexts.
+### Rotating Keys for Encrypted and Signed Cookies
-Encrypted cookies are automatically upgraded if the
-`config.action_dispatch.use_authenticated_cookie_encryption` is enabled.
+It is possible to rotate the `secret_key_base` as well as the salts,
+ciphers, and digests used for both encrypted and signed cookies. Rotating
+the `secret_key_base` is necessary if the value was exposed or leaked.
+It is also useful to rotate this value for other more benign reasons,
+such as an employee leaving your organization or changing hosting
+environments.
-_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!_
+Key rotations can be defined through the
+`config.action_dispatch.cookies_rotations` configuration value. This
+value is set to an instance of
+[RotationConfiguration](http://api.rubyonrails.org/classes/ActiveSupport/RotationConfiguration.html)
+which provides an interface for rotating signed and encrypted cookie
+keys, salts, digests, and ciphers.
-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:
+For example, suppose we want to rotate out an old `secret_key_base`, we
+can define a signed and encrypted key rotation as follows:
- secret_key_base: 492f...
+```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
-If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret.
+config.action_dispatch.cookies_rotations.rotate :signed,
+ digest: "SHA1",
+ secret: Rails.application.credentials.old_secret_key_base,
+ salt: config.action_dispatch.signed_cookie_salt
+```
+
+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`
+
+```ruby
+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
+```
+
+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
+[MessageEncryptor API](api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html)
+and
+[MessageVerifier API](api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html)
+documentation.
### Replay Attacks for CookieStore Sessions