【问题标题】:rails storing password in a sessionrails在会话中存储密码
【发布时间】:2011-11-17 09:26:16
【问题描述】:

我有一个可以进行 web api 调用的 rails 应用程序,rails 应用程序本身没有任何数据库或用户存储。每个 api 调用都需要为每个请求发送用户名和密码。

我想为 rails 应用提供一种身份验证机制。 我打算这样做:

  1. 显示登录页面
  2. 获取用户名和密码
  3. 存储用户名和密码
  4. 通过warden.authenticate 或authlogic.something 执行手动身份验证(或者甚至可能不需要,只需检查会话是否存储了某些内容)
  5. 然后当用户执行某项操作时,我会传递之前存储的用户名和密码。

现在我的问题是我在哪里存储密码? 如果我使用 session 我显然不能使用 cookie 存储,我可以使用 session_store = :active_record_store 但不确定它是否安全,而且我现在还没有任何数据库,为什么我应该只为 session 创建一个? 是否有任何其他机制可以在会话中存储密码? (显然是安全的方式)

早期的铁路有:

  • MemoryStore
  • 文件存储

但现在两者似乎都被删除了。那么还有其他解决方案吗?

答案注释:

  1. 存储加密密码不起作用,因为我需要在进行 api 调用时将原始密码发送到服务器。
  2. 我无法控制 API,因此无法更改其身份验证。
  3. rails 应用程序没有用户配置文件维护。一切都由 API 调用管理。

我终于想到了实现自定义内存存储,但它似乎抛出了 stackoverflow 错误。我从https://rails.lighthouseapp.com/projects/8994/tickets/1876-uninitialized-constant-actioncontrollersessionmemorystore得到代码

require 'action_dispatch'
module ActionDispatch
module Session
class CustomMemoryStore < ActionDispatch::Session::AbstractStore
  GLOBAL_HASH_TABLE = {} #:nodoc:

  private
    def get_session(env, sid)
      sid ||= generate_sid
      session = GLOBAL_HASH_TABLE[sid] || {}
      session = AbstractStore::SessionHash.new(self, env).merge(session)
      [sid, session]
    end

    def set_session(env, sid, session_data)
      GLOBAL_HASH_TABLE[sid] = session_data
      return true
    end
  end
 end
end
Steptools3::Application.config.session_store :custom_memory_store, :key => '_some_xyz'

【问题讨论】:

  • 您可以将加密密码存储在 cookie 中,或将会话存储在服务器端的数据库中。
  • 有点不清楚谁需要登录,在哪个应用程序中。 railsapp(没有分贝?)。另一个应用程序有一个 API?那 webapi 的东西在你的控制之下吗?要延迟对 API 的授权,您通常会使用 OAuth(不必保留用户/密码):让用户登录到其他应用程序,您将收到一个令牌,该令牌将授予您临时访问权限。
  • 我的应用程序的用户输入用户名和密码,rails 应用程序调用 api,说“dosomething”,它调用为“user:asd, pass:qwe,task:dosomething”。因此,rails 应用程序和 api 之间没有身份验证。它只是我必须在每个请求上传递用户名和密码。
  • 你能显示错误的堆栈跟踪吗?请注意,在内存中使用会话存储本质上是不可扩展的:您只能使用一个 rails 进程并且这永远不会改变(您不能在 rails 进程之间共享内存)。
  • 是的,我知道这一点。它只是说SystemStackError stack level too deep

标签: ruby-on-rails session


【解决方案1】:

您可以尝试使用Redis 作为会话存储。我们使用rails3-redis-session-store gem。来源可以找到here

设置非常简单,会话会自动过期,因此很安全。 示例配置:

YourApp::Application.config.session_store :redis_session_store,
                                          :db => 0,
                                          :expire_after => 10.minutes,
                                          :key_prefix => "your_app:session:"

另一种方法是使用dalli,因此使用memcached 作为后端。

希望这会有所帮助。

【讨论】:

  • 不需要自己创建数据库吗?
  • Redis 是一个开源的键值对存储。您确实必须在您的服务器上运行它。您也可以改用 memcached。但同样,这将是您需要在服务器上启动的过程。
  • cool 似乎不错的选择...它将所有内容保存在内存中并在一段时间后将其写回磁盘似乎是一个很好的解决方案,但只是为了在会话中存储一个值而有点过度杀戮。如果没有,将尝试实现 custommemcache(edited answer) 将实现您的方法。谢谢
【解决方案2】:

我建议采取下一步并建立一个简单的数据库,为自己和用户省去很多麻烦,当用户想要返回网站时会发生什么,他们将不得不重新注册。

我发现Devise 非常适合此用途,而且集成起来非常简单。

如果您不想让经典数据库服务器运行,您可能需要查看MongoDB

【讨论】:

  • 没有注册.. 用户是通过 api 创建并通过 api 维护的...... rails 应用程序并没有做所有这些。
【解决方案3】:

会话 cookie 使用会话密钥加密。只要您保持会话密钥强大(128 个字符)且安全,您的数据就应该是安全的。

ActionController::Base.session = {
  :key         => '_foo_bar_session',
  :http_only   => true,
  :secret      => 'dldkdke420934indsknknkfsnh318u84e9u49832dfkdsajdsk'
}

如果您想在浏览器会话之外存储身份验证详细信息,则可以将它们存储在签名的永久 cookie 中。

cookies.permanent.signed[:user_credentials] = [login, password]

签名的 cookie 像普通 cookie 一样被访问:

cookies[:user_credentials]

确保在初始化文件中设置一个强 cookie_verifier_secret

ActionController::Base.cookie_verifier_secret ='dskjkjfdshfddsfkhkr3898398430943'

参考

Signed and Permanent cookies in Rails 3

【讨论】:

  • 我正在尝试使用这个:rails.lighthouseapp.com/projects/8994/tickets/…。但我收到堆栈溢出错误。像这样module ActionDispatch module Session class CustomMemoryStore &lt; ActionDispatch::Session::AbstractStore 和网站上的一样
  • @GauravShah 您是否尝试使用签名 cookie 或会话 cookie?会话 cookie 默认是签名的。因此,如果您不必在浏览器会话之外保留凭据,则可以使用它们。
  • @GauravShah 你的 Rails 版本是什么?你在使用 cookie 会话存储吗? (错误信息表明您正在使用 MemoryStore。)
  • 我理解你关于 cookieStore 的观点,它几乎符合我的需要,除了一点安全问题。所以我想从旧的 Rails 中恢复 MemoryStore 并使用它。我正在尝试使用上面评论中的链接实现customMemoryStore,当我使用该自定义内存存储时,我得到stackOverFlowError
  • 请查看修改后的答案,您就会明白我要解释的内容。 @KandadaBoggu
【解决方案4】:

我会尝试分析你的选择:

如果服务器被 CustomMemoryStore 入侵

考虑以下场景:

  • 4 个用户 A、B、C、D 已登录。
  • 您的服务器被黑了。黑客获得了服务器的控制权。
  • D 做了一些操作。
  • 您发现您的服务器被黑客入侵,并修复了您的系统。

使用CustomMemoryStore,黑客可以获得所有用户的密码。在运行的 Rails 进程中注入一些逻辑,或者转储内存和分析并不难。在 ActiveRecord、MongoDB、Redis 中存储密码也有类似的问题。

如果服务器被 CookieStore 入侵?

如果发生前面的场景并且您正在使用 CookieStore 怎么办?

让我们回顾一下CookieStore的机制:

  • 服务器有一个密钥来签署和验证会话。
  • 每次浏览器发送请求时,服务器都会对会话进行解密,修改数据,对会话进行签名,并在cookie中将会话发送给浏览器。

换句话说,黑客无法从 cookie 或密钥中获取密码。他需要 cookie 和密钥来窃取密码。

在这种情况下,A、B、C 的密码是安全的。只有 D 的密码会被黑客窃取。您可以通过尽快修复系统来最大限度地减少损坏。

CustomMemoryStore 的问题

除了安全问题,我知道您知道 CustomMemoryStore 不可扩展。但是,问题可能比您想象的要大。您将在控制器操作中向其他 Web 服务发送请求,如果远程服务速度慢或速度下降,它将阻塞您的整个服务器。即使您只有 1~10 个并发用户,也可能会很痛苦。

即使您决定在单个服务器上运行您的应用程序,您也可以使用Passenger 或Unicorn 启动多个rails 进程。 CustomMemoryStore 拒绝这些选项。

客户安全问题

如果担心cookie是否从浏览器端被盗,您可以考虑EncryptedCookieStore。它加密会话并存储在客户端 cookie 中。如果您只有 cookie 或密钥,则无法获取密码。您需要 cookie 和密钥来解密密码。

关键问题是什么?

EncryptedCookieStore 更安全,因为它将加密的密码存储在用户的 cookie 中,并且密钥仅在服务器上可用。如果只有 cookie 或密钥,黑客就无法获得密码——他需要两者。

当然,您可以使用 CustomMemoryStore 实现类似的逻辑。例如,将加密密码存储在服务器内存中,而单个密钥在 cookie 中。如果你仍然决定在服务器上存储加密密码,我会推荐使用 Redis 进行存储。与 MySQL 和 MongoDB 相比,它简单快速。由于缩放问题,不建议使用 CustomMemoryStore。

其他建议

其他系统的密码是非常敏感的数据,你应该非常小心处理安全问题。如果它是一项公共服务,您应该非常仔细地编写您的服务条款和免责声明协议。此外,您应该使用 HTTPS 运行您的服务。

TL;DR

  • 尽可能使用 OAuth(好吧,我知道你不能)
  • EncryptedCookieStore 应该简单且安全。
  • 如果您决定将密码存储在服务器上,请将其加密并将密钥存储在客户端(cookie)上。

【讨论】:

  • 我不能使用 OAuth,因为 API 不在我的控制之下。会考虑你提到的。虽然我不相信客户端 cookie 的安全性..
  • 很抱歉我之前的回答有些错误。修复它并添加“客户安全问题”部分。
猜你喜欢
  • 2012-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多