问题 检测Rails 4会话cookie篡改


背景

我是一位经验丰富的Web开发人员(主要使用Python和CherryPy),他之前已经从头开始实施安全会话管理,现在正在学习Rails。我正在研究Rails会话的行为 session 可用的对象 ActionController 实例和视图上下文。

问题/问题

我已经读过Rails 4中的会话的默认实现使用加密和防篡改的cookie。很酷,我想这意味着我可以使用它来保存用户会话的用户ID,而不必担心会话伪造(防篡改)或任何人能够找出他们的ID(加密)。我想对此进行测试,看看如果会话cookie被更改,rails会做什么。

因此,我使用浏览器插件改变了会话cookie属性的内容,当我使用新的cookie值重新加载页面时,Rails很高兴为我提供了不同的新值 session_id 和 _csrf_token

会话cookie完整性发生了什么变化!?

铁路不应该检测(通过HMAC签名)cookie被改变,然后以某种方式告诉我它?

我很害怕我错过了一些非常明显的东西,但我一直没有在网上搜索答案,而且源代码也没有轻易放弃(我是ruby新手)。提前致谢。

本实验

我创建了一个新的应用程序并生成了一个控制器 index 行动:

$ rails new my_app
$ cd my_app; rails g controller home index

然后我将这两行添加到了 应用程序/视图/布局/ application.html.erb 文件:

<%= session.keys %><br/>
<%= session.values %>

我启动了开发服务器并将浏览器导航到“localhost:3000 / home / index”。正如预期的那样,页面底部有以下几行:

["session_id", "_csrf_token"]
["8c1558cabe6c86cfb37d6191f2e03bf8", "S8i8/++8t6v8W8RMeyvnNu3Pjvj+KkMo2UEcm1oVVZg="]

重新加载页面给了我相同的值,虽然应用程序设置了新的值 _my_app_session 每次都有cookie属性。这对我来说似乎很奇怪,但我得到了相同的会话哈希值,所以我觉得它很酷。

然后,我使用Chrome编辑附加​​组件来改变Chrome的价值 _my_app_session cookie属性(替换属性值的第一个字符)。重新加载页面会显示完全不同的值,而不会发生任何事情。 WAT?


6370
2017-09-11 21:26


起源



答案:


我不能要求对这里的代码有一个非常透彻的理解。但我可以告诉你这么多:

我完全按照你的步骤(使用Ruby 2.0.0-p247和Rails 4.0),但有一个例外 - 我还在我的Gemfile中添加了'byebug'gem并在其中添加了一个调试断点。 HomeController#index 行动。

从byebug控制台,在那个断点处,我可以看到 未经编辑 会话cookie通过:

(byebug) cookies["_my_app_session"]
"cmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"

我可以看到实际的加密值

(byebug) cookies.encrypted["_my_app_session"]
{"session_id"=>"13a95fb545a1e3a2d4e9b4c22debc260", "_csrf_token"=>"FXb8pZgmoK0ui0qCW8W75t3sN2KLRpkiFBmLbHSfnhc="}

现在,我通过将第一个字母更改为“A”来编辑cookie并刷新页面:

(byebug) cookies["_my_app_session"]
"AmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"
(byebug) cookies.encrypted["_my_app_session"]
nil

所以会议是 nil 在此请求中:

(byebug) session
#<ActionDispatch::Request::Session:0x7ff41ace4bc0 not yet loaded>

我可以强制加载会话

(byebug) session.send(:load!)

当我这样做时,我看到结果会话ID是

"f6be13fd646962de676985ec9bb4a8d3"

当然,当我让请求结束时,这就是我在视图中看到的内容:

["session_id", "_csrf_token"] ["f6be13fd646962de676985ec9bb4a8d3", "qJ/aHzovZYpbrelGpRFec/cNlJyWjonXDoOMlDHbWzg="]

我现在也有一个新的cookie值,与我编辑的那个无关。

因此,我认为我们可以得出结论,正在发生的事情是,由于无法验证cookie签名,因此会话无效并重新生成。我现在有一个新的会话,使用不同的csrf_token。

相关代码出现在 actionpack/lib/action_dispatch/middleware/cookies.rb:460-464, 在里面 EncryptedCookieJar 类:

def decrypt_and_verify(encrypted_message)
  @encryptor.decrypt_and_verify(encrypted_message)
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
  nil
end

我们只是将其视为具有无效签名的消息,而不是解密消息 nil。因此,存储会话ID和csrf令牌的无法验证的cookie不会用于加载会话,并且依赖于cookie中的值的任何内容都将失败。

那么为什么我们不会得到错误而不仅仅是新会话?那是因为我们没有尝试任何取决于加密值的东西。特别是,虽然我们有

protect_from_forgery with: :exception

(而不是 :null_sessionApplicationController,Rails不会在GET或HEAD请求上验证csrf令牌 - 它依赖于开发人员根据规范实现这些操作,因此它们是非破坏性的。如果您在POST请求中尝试了相同的操作,那么您将得到一个 ActionController::InvalidAuthenticityToken 错误(因为您可以轻松验证自己)。


13
2017-09-12 14:39



Oy公司。不知道为什么我坚持认为会话被改变而不是被替换的想法。现在它完全有道理。为了确保我做了一个跟进实验:添加一个添加了一个控制器的控制器 user_id 归因于 session 哈希值。在篡改会话cookie属性值后,导航回“home / index”会显示没有的新会话值 user_id 关键 - 表示一个 新 会议,而不仅仅是一个改变。再次感谢! - mhess
没问题,我实际上并不知道你的问题的答案,并受到启发调查! - gregates


答案:


我不能要求对这里的代码有一个非常透彻的理解。但我可以告诉你这么多:

我完全按照你的步骤(使用Ruby 2.0.0-p247和Rails 4.0),但有一个例外 - 我还在我的Gemfile中添加了'byebug'gem并在其中添加了一个调试断点。 HomeController#index 行动。

从byebug控制台,在那个断点处,我可以看到 未经编辑 会话cookie通过:

(byebug) cookies["_my_app_session"]
"cmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"

我可以看到实际的加密值

(byebug) cookies.encrypted["_my_app_session"]
{"session_id"=>"13a95fb545a1e3a2d4e9b4c22debc260", "_csrf_token"=>"FXb8pZgmoK0ui0qCW8W75t3sN2KLRpkiFBmLbHSfnhc="}

现在,我通过将第一个字母更改为“A”来编辑cookie并刷新页面:

(byebug) cookies["_my_app_session"]
"AmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"
(byebug) cookies.encrypted["_my_app_session"]
nil

所以会议是 nil 在此请求中:

(byebug) session
#<ActionDispatch::Request::Session:0x7ff41ace4bc0 not yet loaded>

我可以强制加载会话

(byebug) session.send(:load!)

当我这样做时,我看到结果会话ID是

"f6be13fd646962de676985ec9bb4a8d3"

当然,当我让请求结束时,这就是我在视图中看到的内容:

["session_id", "_csrf_token"] ["f6be13fd646962de676985ec9bb4a8d3", "qJ/aHzovZYpbrelGpRFec/cNlJyWjonXDoOMlDHbWzg="]

我现在也有一个新的cookie值,与我编辑的那个无关。

因此,我认为我们可以得出结论,正在发生的事情是,由于无法验证cookie签名,因此会话无效并重新生成。我现在有一个新的会话,使用不同的csrf_token。

相关代码出现在 actionpack/lib/action_dispatch/middleware/cookies.rb:460-464, 在里面 EncryptedCookieJar 类:

def decrypt_and_verify(encrypted_message)
  @encryptor.decrypt_and_verify(encrypted_message)
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
  nil
end

我们只是将其视为具有无效签名的消息,而不是解密消息 nil。因此,存储会话ID和csrf令牌的无法验证的cookie不会用于加载会话,并且依赖于cookie中的值的任何内容都将失败。

那么为什么我们不会得到错误而不仅仅是新会话?那是因为我们没有尝试任何取决于加密值的东西。特别是,虽然我们有

protect_from_forgery with: :exception

(而不是 :null_sessionApplicationController,Rails不会在GET或HEAD请求上验证csrf令牌 - 它依赖于开发人员根据规范实现这些操作,因此它们是非破坏性的。如果您在POST请求中尝试了相同的操作,那么您将得到一个 ActionController::InvalidAuthenticityToken 错误(因为您可以轻松验证自己)。


13
2017-09-12 14:39



Oy公司。不知道为什么我坚持认为会话被改变而不是被替换的想法。现在它完全有道理。为了确保我做了一个跟进实验:添加一个添加了一个控制器的控制器 user_id 归因于 session 哈希值。在篡改会话cookie属性值后,导航回“home / index”会显示没有的新会话值 user_id 关键 - 表示一个 新 会议,而不仅仅是一个改变。再次感谢! - mhess
没问题,我实际上并不知道你的问题的答案,并受到启发调查! - gregates