How to decrypt a Rails 5 session cookie manually?

我与影子孤独终老i 提交于 2019-12-18 05:57:10

问题


I have access to

  • config.action_dispatch.encrypted_cookie_salt
  • config.action_dispatch.encrypted_signed_cookie_salt
  • secrets.secret_key_base
  • the full cookie string (including --)

I see ways to do this in Rails 4 (Rails 4: How to decrypt rails 4 session cookie (Given the session key and secret)), but these don't seem to work in Rails 5.


回答1:


I have had the same problem the other day and figured out that the generated secret was 64 bytes long (on my mac), but Rails ensures that the key is 32 bytes long (source).

This has worked for me:

require 'cgi'
require 'json'
require 'active_support'

def verify_and_decrypt_session_cookie(cookie, secret_key_base)



cookie = CGI::unescape(cookie)
  salt         = 'encrypted cookie'
  signed_salt  = 'signed encrypted cookie'
  key_generator = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
  secret = key_generator.generate_key(salt)[0, ActiveSupport::MessageEncryptor.key_len]
  sign_secret = key_generator.generate_key(signed_salt)
  encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: JSON)

  encryptor.decrypt_and_verify(cookie)
end

Or without ActiveSupport:

require 'openssl'
require 'base64'
require 'cgi'
require 'json'

def verify_and_decrypt_session_cookie cookie, secret_key_base
  cookie = CGI.unescape(cookie)

  #################
  # generate keys #
  #################
  encrypted_cookie_salt = 'encrypted cookie' # default: Rails.application.config.action_dispatch.encrypted_cookie_salt
  encrypted_signed_cookie_salt = 'signed encrypted cookie' # default: Rails.application.config.action_dispatch.encrypted_signed_cookie_salt
  iterations = 1000
  key_size = 64
  secret = OpenSSL::PKCS5.pbkdf2_hmac_sha1(secret_key_base, encrypted_cookie_salt, iterations, key_size)[0, OpenSSL::Cipher.new('aes-256-cbc').key_len]
  sign_secret = OpenSSL::PKCS5.pbkdf2_hmac_sha1(secret_key_base, encrypted_signed_cookie_salt, iterations, key_size)

  ##########
  # Verify #
  ##########
  data, digest = cookie.split('--')
  raise 'invalid message' unless digest == OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, sign_secret, data)
  # you better use secure compare instead of `==` to prevent time based attact,
  # ref: ActiveSupport::SecurityUtils.secure_compare

  ###########
  # Decrypt #
  ###########
  encrypted_message = Base64.strict_decode64(data)
  encrypted_data, iv = encrypted_message.split('--').map{|v| Base64.strict_decode64(v) }
  cipher = OpenSSL::Cipher.new('aes-256-cbc')
  cipher.decrypt
  cipher.key = secret
  cipher.iv  = iv
  decrypted_data = cipher.update(encrypted_data)
  decrypted_data << cipher.final

  JSON.load(decrypted_data)
end

Feel free to comment on the gist: https://gist.github.com/mbyczkowski/34fb691b4d7a100c32148705f244d028




回答2:


Here's a Rails 5.2 variant of @matb's answer, which handles the revised configuration, encryption and serialization:

require 'cgi'
require 'active_support'

def verify_and_decrypt_session_cookie(cookie, secret_key_base = Rails.application.secret_key_base)
  cookie = CGI::unescape(cookie)
  salt   = 'authenticated encrypted cookie'
  encrypted_cookie_cipher = 'aes-256-gcm'
  serializer = ActiveSupport::MessageEncryptor::NullSerializer

  key_generator = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000)
  key_len = ActiveSupport::MessageEncryptor.key_len(encrypted_cookie_cipher)
  secret = key_generator.generate_key(salt, key_len)
  encryptor = ActiveSupport::MessageEncryptor.new(secret, cipher: encrypted_cookie_cipher, serializer: serializer)

  encryptor.decrypt_and_verify(cookie)
end

Also up at https://gist.github.com/inopinatus/e523f36b468f94cf6d34410b73fef15e.



来源:https://stackoverflow.com/questions/41474176/how-to-decrypt-a-rails-5-session-cookie-manually

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!