Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Decrypt Rails 6.0 beta session cookies
require 'cgi'
require 'active_support'
def verify_and_decrypt_session_cookie(cookie, secret_key_base = Rails.application.secret_key_base)
config = Rails.application.config
cookie = CGI::unescape(cookie)
salt = config.action_dispatch.authenticated_encrypted_cookie_salt
encrypted_cookie_cipher = config.action_dispatch.encrypted_cookie_cipher || 'aes-256-gcm'
# serializer = ActiveSupport::MessageEncryptor::NullSerializer # use this line if you don't know your serializer
serializer = ActionDispatch::Cookies::JsonSerializer
key_generator =, iterations: 1000)
key_len = ActiveSupport::MessageEncryptor.key_len(encrypted_cookie_cipher)
secret = key_generator.generate_key(salt, key_len)
encryptor =, cipher: encrypted_cookie_cipher, serializer: serializer)
session_key = config.session_options[:key].freeze
encryptor.decrypt_and_verify(cookie, purpose: "cookie.#{session_key}")
Copy link

lisbethw1130 commented Dec 14, 2021

this helps a lot, thanks a lot 👍

Copy link

aizotov commented Dec 27, 2021

This is perfect 🙏 blessing be upon you

Copy link

LeKhoa commented Jan 12, 2022

I use this method but it shows below error when I call encryptor.decrypt_and_verify

*** ActiveSupport::MessageEncryptor::InvalidMessage Exception: ActiveSupport::MessageEncryptor::InvalidMessage

Copy link

theblang commented Jan 27, 2022

@LeKhoa Ditto, did you ever figure it out?

Update: Ahh, I realized that I was calling the method with request.cookies['cookie_name'], which is an unescaped cookie value, when really the logic wants the escaped value (i.e. the one you can copy from browser devtools). See the line: cookie = CGI::unescape(cookie).

After realizing that, I still had an error (a different one) when trying to decrypt. It ended up being the fact that our old Rails app is using :marshal as the cookies_serializer config (see this doc), for which I needed to instead use the line: serializer = ActiveSupport::MessageEncryptor::NullSerializer.

Note that you'll also want to either JSON.parse or Marshal.restore (again, depending on your serializer) the value returned from decrypt_and_verify.

Copy link

Eric-Guo commented May 6, 2022

I confirm above function verify_and_decrypt_session_cookie works perfect in Rails 6.1.5, but meet Rails 7 error as below.

irb(main):013:0> encryptor.decrypt_and_verify(cookie, purpose: "cookie.#{session_key}")
/var/www/matlib/shared/bundle/ruby/3.1.0/gems/activesupport- `rescue in _decrypt': ActiveSupport::MessageEncryptor::InvalidMessage (ActiveSupport::MessageEncryptor::InvalidMessage)
/var/www/matlib/shared/bundle/ruby/3.1.0/gems/activesupport- `final': OpenSSL::Cipher::CipherError

Rails.application.config.action_dispatch.cookies_serializer is :json and CGI::unescape(cookie) also called.

Copy link

Eric-Guo commented May 6, 2022

For anyone search and reach here, Demystifying cookie security in Rails 6 works both Rails 6 and 7 and below code snippets is copy from the article and backup purpose in case URL is break in future.

cookie = "aDkxgmW4kaxoXBGnjxAaBY7D47WUOveFdeai5kk2hHlYVqDo7xtzZJup5euTdH5ja5iOt37MMS4SVXQT5RteaZjvpdlA%2FLQi7IYSPZLz--2A6LCUu%2F5AsLfSez--QD%2FwiA2t8QQrKk6rrROlPQ%3D%3D"
cookie = CGI.unescape(cookie)
data, iv, auth_tag = cookie.split("--").map do |v| 
cipher ="aes-256-gcm")

# Compute the encryption key
secret_key_base = Rails.application.secret_key_base
secret = OpenSSL::PKCS5.pbkdf2_hmac_sha1(secret_key_base, "authenticated encrypted cookie", 1000, cipher.key_len)

# Setup cipher for decryption and add inputs
cipher.key = secret
cipher.iv  = iv
cipher.auth_tag = auth_tag
cipher.auth_data = ""

# Perform decryption
cookie_payload = cipher.update(data)
cookie_payload <<
cookie_payload = JSON.parse cookie_payload
# => {"_rails"=>{"message"=>"InRva2VuIg==", "exp"=>nil, "pur"=>"cookie.remember_token"}}

# Decode Base64 encoded stored data
decoded_stored_value = Base64.decode64 cookie_payload["_rails"]["message"]
stored_value = JSON.parse decoded_stored_value
# => "token"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment