给定会话密钥和秘密,我们如何解密 Rails cookie?

Posted

技术标签:

【中文标题】给定会话密钥和秘密,我们如何解密 Rails cookie?【英文标题】:Given the session key and secret, how can we decrypt Rails cookies? 【发布时间】:2010-12-28 19:54:13 【问题描述】:

我有一个关于 Rails 如何处理 cookie 的问题 加密/解密。

我的 config/environment.rb 中有这个

  config.action_controller.session = 
    :session_key => [some key],
    :secret => [some secret]
  

这在 config/environment/production.rb 等:

  ActionController::Base.session_options[:session_domain] = [some
domain]

到目前为止,一切都很好——只要我所有的 Rails 应用程序都一样 session_key 和 secret,并且在同一个域上,它们都可以使用 同样的 cookie。

但是,一位同事现在有一个 JSP 应用程序(在同一个域上), 他想用它来读取我设置的 cookie。

那么,给定一个秘密和一个加密的 cookie 值,我们将如何解密 它来获取那个cookie的内容?

(文档似乎表明这是默认情况下的单向 SHA1 加密 -- http://caboo.se/doc/classes/CGI/Session/CookieStore.html -- 但是 那么我的 Rails 应用程序将如何读取 cookie 的内容 那是单向加密的吗?)

提前感谢您提供的任何提示/指针/见解,

【问题讨论】:

railshorde.com/blog/rails-decode-session-cookie 【参考方案1】:

Rails 使用 HMAC-SHA1 加密 cookie 数据,这与您所怀疑的单向 SHA1 加密不同(请参阅 the Wikipedia article on HMAC 以获得解释)。加密由ActiveSupport::MessageVerifier 类完成(源代码可读性强)。下面是一个基于测试 Rails 应用的示例:

secret = 'b6ff5a9c3c97bf89afe9a72e6667bafe855390e8570d46e16e9760f6394' +
  '4ab05577b211ec2f43f6c970441518f0241775499bde055078f754c33b62f68ba27ca'

cookie = "_test_session=BAh7CCIYdXNlcl9jcmVkZW50aWFsc19pZGkGIhV1c2VyX2NyZW" +
  "RlbnRpYWxzIgGAMzBlODkxZDQ2MWZhNjFkMDFmNzczMmJjNDdjMjIwZGFjMTY2NWEwNDMwZ" +
  "DVjMmUxOWY5MDFjMjQ5NWQ4OTM1OGZlMzE3NzRiZTFiZjM1ZTVlZDY2ZGUzYTkwOWZjZTgw" +
  "NTFlNGUxZWI0MTUzYTRjODZiMGZmMzM3NzliM2U3YzI6D3Nlc3Npb25faWQiJTgxNzk0Yjd" +
  "kN2IxYzRjMDE0M2QwOTk5NTVjZjUwZTVm--25c8f3222ab1be9f2394e2795a9f2557b06d0a92"

session = cookie.split('=').last
verifier = ActiveSupport::MessageVerifier.new(secret, 'SHA1')
verifier.verify(session)

这应该返回您期望的会话哈希。要在 Java 中实现这一点,您的同事将不得不复制 ActiveSupport::MessageVerifier#verify 方法。源代码位于您的 gems 目录中(我的系统上为 /usr/lib/ruby/gems/1.8/gemsactivesupport-2.3.5/lib/active_support/message_verifier.rb

【讨论】:

我认为您的回答不太正确。 HMAC-SHA1 是一种用于验证数据字符串真实性的算法。它的工作原理是使用单向哈希函数计算消息的摘要,并在创建消息时生成摘要。没有进行加密。 如果您收到错误:ArgumentError: wrong number of arguments (given 3, expected 1..2)。这是因为在 Rails 6 中,MessageVerifier 的参数发生了变化。使用ActiveSupport::MessageVerifier.new(secret, digest: 'SHA1')。您也可以完全取消 digest 选项,因为它默认为 SHA1【参考方案2】:

如果您直接从应用数据库中存储的会话数据中提取 session.data 字段(如果您在 environment.rb 文件中使用 active_record_store)

config.action_controller.session_store = :active_record_store

...这里是你如何解码它并返回哈希:

Marshal.load(ActiveSupport::Base64.decode64(@session.data))

... 或在 Rails 中 >= 3.2(感谢 Chuck Vose)

Marshal.load(Base64.decode64(@session.data))

根本没有加密。

【讨论】:

唉,我正在为这个特定项目的会话使用 cookie。我在您的行中没有看到任何特定于数据库的内容来对其进行解码。 “解密”是错误的词;我应该说“解码”。感谢您的建议。 在 Rails3.2 中 ActiveSupport 不再是 Base64 的父级。只需删除 ActiveSupport:: 即可完成这项工作。另外值得注意的是,如果你正在调试,如果它给你带来麻烦,你可以离开 Marshal.load。【参考方案3】:

默认情况下,Rails(版本 4 之前)不加密会话 cookie,它只对它们进行签名。要加密它们,您需要执行以下操作:

ActionController::Base.session_store = EncryptedCookieStore

有多个插件可以提供这种加密功能。

因此,如果您不是专门使用加密存储,Java 代码所需要做的就是验证 cookie 签名并解码 cookie。正如 Alex 在他的回答中所说,您需要复制 ActiveSupport::MessageVerifier#verify 的功能,并与 Java 应用程序共享密钥。既验证又解码 cookie。

如果您不想验证签名(我不推荐),您可以使用 Midwire 的 Base64 解码方法来查看会话哈希。在 Ruby 中,这是:

Marshal.load(ActiveSupport::Base64.decode64(the_cookie_value))

我知道这是旧的,但希望这对某人有帮助!

(更新:这个问题与 Rails 3 有关。从 Rails 4 开始,默认情况下会话 cookie 是加密的。)

【讨论】:

【参考方案4】:

这是在 Rails 4 中解密会话 cookie 的方法

def decrypt_session_cookie(cookie)
  cookie = CGI.unescape(cookie)
  config = Rails.application.config

  encrypted_cookie_salt = config.action_dispatch.encrypted_cookie_salt               # "encrypted cookie" by default
  encrypted_signed_cookie_salt = config.action_dispatch.encrypted_signed_cookie_salt # "signed encrypted cookie" by default

  key_generator = ActiveSupport::KeyGenerator.new(config.secret_key_base, iterations: 1000)
  secret = key_generator.generate_key(encrypted_cookie_salt)
  sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt)

  encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
  encryptor.decrypt_and_verify(cookie)
end

http://big-elephants.com/2014-01/handling-rails-4-sessions-with-go/

【讨论】:

在创建 ActiveSupport::KeyGenerator 以使其工作时,我必须使用 Rails.application.secrets.secret_key_base 而不是 config.secret_key_base(后者在开发环境中为零)。【参考方案5】:

我编写了一个 Ruby gem 来处理由 Rails 应用程序管理的 cookie。阅读它的源代码,您可以了解它的工作原理,并可能将其移植到 Java,以便您的 JSP 应用程序可以使用它:

https://github.com/rosenfeld/rails_compatible_cookies_utils

它是一个包含约 150 行代码的单个文件,它还处理仅签名的 cookie 值并负责签名/加密和验证/解密,而您似乎只关心解密。这是解密的方法:

https://github.com/rosenfeld/rails_compatible_cookies_utils/blob/master/lib/rails_compatible_cookies_utils.rb#L41-L52

值得一提的是,除了密钥和秘密之外,您还需要知道使用了哪个序列化程序。它曾经是 Marshal,但现在新生成的应用程序的默认值似乎是 JSON。如果使用 Marshal,那么将该代码转换为 Java 可能会很棘手,因为您必须找到一个实现 Ruby 的 Marshal#load 的库。

【讨论】:

这非常有用,谢谢。检查源代码要容易得多,因为它与其他解决方案相比更紧凑,并帮助我在 Elixir 中复制了您的功能。 是的,这就是我的想法。我认为愿意在其他语言中实现相同逻辑的人会受益于将逻辑全部集中在一个点上。很高兴它帮助了你:)

以上是关于给定会话密钥和秘密,我们如何解密 Rails cookie?的主要内容,如果未能解决你的问题,请参考以下文章

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

Https 密钥协商中的第三个随机数pre-master-key如何防篡改?

三秘密的好处

Java AES加密和静态密钥解密

无法使用 OpenPGP.js 解密未装甲的 pgp 文件:会话密钥解密失败

DH 迪菲-赫尔曼密钥交换