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

问题描述 投票:0回答:3

我可以访问

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

我看到了在 Rails 4 中执行此操作的方法(Rails 4:如何解密 Rails 4 会话 cookie(给定会话密钥和秘密)),但这些似乎在 Rails 5 中不起作用。

ruby-on-rails encryption cookies ruby-on-rails-5 session-cookies
3个回答
38
投票

前几天我也遇到了同样的问题,发现生成的秘密是 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


7
投票

这是 @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


0
投票

对于任何寻找简单“即插即用”解决方案的人,我创建了一个 gem,它允许您以与 Rails 相同的方式轻松加密/解密数据:

宝石这里

动机在这里

© www.soinside.com 2019 - 2024. All rights reserved.