如何手动解密 Rails 5 会话 cookie?

Posted

技术标签:

【中文标题】如何手动解密 Rails 5 会话 cookie?【英文标题】:How to decrypt a Rails 5 session cookie manually? 【发布时间】:2017-05-19 08:31:45 【问题描述】:

我可以访问

config.action_dispatch.encrypted_cookie_salt config.action_dispatch.encrypted_signed_cookie_salt secrets.secret_key_base 完整的 cookie 字符串(包括 --

我在 Rails 4 (Rails 4: How to decrypt rails 4 session cookie (Given the session key and secret)) 中看到了执行此操作的方法,但这些方法似乎在 Rails 5 中不起作用。

【问题讨论】:

【参考方案1】:

前几天我遇到了同样的问题,发现生成的密钥长度为 64 字节(在我的 Mac 上),但 Rails 确保密钥长度为 32 字节(source)。

这对我有用:

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

或者没有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

请随意评论要点:https://gist.github.com/mbyczkowski/34fb691b4d7a100c32148705f244d028

【讨论】:

【参考方案2】:

这是@matb 答案的 Rails 5.2 变体,它处理修改后的配置、加密和序列化:

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

同样在https://gist.github.com/inopinatus/e523f36b468f94cf6d34410b73fef15e。

【讨论】:

以上是关于如何手动解密 Rails 5 会话 cookie?的主要内容,如果未能解决你的问题,请参考以下文章

如何手动解密 ASP.NET Core 身份验证 cookie?

Ruby on Rails 中的会话如何工作?

Rails - 多个***域和单个会话/cookie

Rails 3 禁用会话 cookie

在 Rails 中的子域之间共享会话(cookie)?

ruby 在Rails 3中解码会话Cookie