Rails 4 Not Encrypting Cookie Contents

Rails 4 not encrypting cookie contents

Your cookis is not encrypted, because you used the signed method on the cookie jar, which, well, just signes the cookie content.

To encrypt the cookie, use the encrypted method:

cookies.encrypted[:discount] = 45
# => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/

cookies.encrypted[:discount] # => 45

How to encrypt the session cookie in rails 4

After reviewing the relevant Rails Documentation, This area provides the answer:

If you only have secret_token set, your cookies will be signed, but not encrypted. This means a user cannot alter their user_id without 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 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.

secret_key_base is located in rails 4 by default in: config/secrets.yml. So actually: in rails 4 the default actually is to encrypt session cookies.

Rails 4: How to decrypt rails 4 session cookie (Given the session key and secret)

Rails 4 uses AES-256 to encrypt cookies with the key based on your app's secret_token_base.

Here's the general scheme of decrypting a session cookie:

  1. calc your secret key
  2. Base 64 decode the cookie value
  3. split the decoded cookie value by '--', this will result in two parts, the first part is the encrypted data and the second is the initialization vector used by the encryption scheme. Base 64 decode each part independently.
  4. decrypt the encrypted data by applying AES decryption with the secret key and the initialization vector.

I couldn't find a website to easily decrypt the messages (advice is welcome), programmatically it can be done like this:

secret = OpenSSL::PKCS5.pbkdf2_hmac_sha1(app_secret_token, 'encrypted cookie', 1000, 64)

encrypted_message = Base64.decode64(cookie_str)
cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}

cipher.decrypt
cipher.key = secret
cipher.iv = iv

decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final

Marshal.load(decrypted_data)

Couple of notes:

  • This code snippet is almost identical to the actual _decript method implementation in ActiveSupport::MessageEncryptor which is used by the ActionDispatch::Cookies middelware.

  • This is all very much Rails 4 specific, from the ActionDispatch::Session::CookieJar:

    If you only have secret_token set, your cookies will be signed, but not encrypted. This means a user cannot alter their +user_id+ without 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
    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.

Storing an encrypted cookie with Rails

I'm re-posting JacobM's answer, that he deleted, because it was the correct answer and pointed me in the right direction. If he undeletes it, I'll delete this one and pick his as the best answer.

First of all, if you use encrypt_and_verify instead of encrypt it will
sign the cookie for you.

However, when it comes to security, I always prefer to rely on
solutions that have been vetted in public, rather than rolling my own.
An example would be the encrypted-cookies gem.

What is the difference between signed and encrypted cookies in Rails?

It's subtle, but the answer is in the documentation you provided. Signed cookies only guard against tampering, while encrypted cookies guard against reading and tampering.

More specifically, signed cookies call ActiveSupport::MessageVerifier to append a digest (generated using secret_key_base) to the cookie. If the value of the cookie is modified, the digest will no longer match, and without knowing the value of secret_key_base, the cookie cannot be signed. The value of the cookie is merely base64 encoded, however, and can be read by anyone.

Encrypted cookies called ActiveSupport::MessageEncryptor to actually encrypt the value of the cookie before generating the digest. Similar to signed cookies, if the value of cookie is modified the digest will no longer match, but additionally the value of the cookie cannot be decrypted without the secret_key_base.

As to when you'd use encrypted versus signed cookies, it comes down to the sensitivity of the information you're storing in the cookie. If all you want to protect against is someone modifying the cookie, then sign it - but if you also need to keep the data secret, encrypt it.

Rails 4 Encrypted Cookie Replay Attack

After some research and some tinkering, I have come up with the following solution.

  • When user logs in, create a random secret (random in the sense that subsequent secrets should have a low probability of matching)
  • Store that secret in the session, i.e. in the cookie, as well as server side, I'm using the Dalli gem to provide memcached functionality
  • On a request for a page that requires authentication, read the secret from the cookie, and make sure it exists server side
  • On logout, delete secret from cache, so any subsequent requests using the same cookies will be invalidated

As long as the cookies cannot be tampered with, then this should be secure. Any thoughts/comments are welcome



Related Topics



Leave a reply



Submit