Rails将密码存储在会话中

6
我有一个Rails应用程序,它进行Web API调用,Rails应用程序本身没有任何数据库或用户存储。每个API调用都需要为每个请求发送用户名和密码。
我想为Rails应用程序提供身份验证机制。 我计划这样做:
1. 显示登录页面 2. 获取用户名和密码 3. 存储用户名和密码 4. 手动进行身份验证,可以通过warden.authenticate或authlogic.something执行(或者甚至不需要,只需检查会话中是否有存储的内容) 5. 然后当用户执行某些操作时,我传递之前存储的用户名和密码。
现在我的问题是,我应该在哪里存储密码? 如果我使用会话,我显然不能使用cookie存储,我可以使用session_store = :active_record_store,但不确定它是否安全,而且我现在没有任何数据库,所以为什么我要创建一个只是为了会话的数据库? 是否有其他机制可以在会话中存储密码?(显然是安全的)
早期的Rails有:
1. MemoryStore 2. FileStore 但现在似乎都被删除了。还有其他解决方案吗?
答案附注:
  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中,或者将会话存储在服务器端的数据库中。 - Dave Newton
有点不清楚是谁需要在哪个应用程序中登录。Railsapp(没有数据库?)。另一个应用程序有一个API?那个Webapi的东西在你的控制之下吗?如果要将授权推迟到API,通常会使用OAuth(以避免保留用户/密码):让用户登录到其他应用程序,您将收到一个令牌,该令牌将授予您临时访问权限。 - nathanvda
我的应用程序用户输入用户名和密码,Rails应用程序调用API,执行“dosomething”,它的调用方式为“user:asd,pass:qwe,task:dosomething”。Rails应用程序和API之间没有身份验证,每个请求都必须传递用户名和密码。 - Gaurav Shah
你能展示一下错误的堆栈吗?请注意,在内存中使用会话存储本质上是不可扩展的:你只能使用一个Rails进程,这永远不会改变(你不能在Rails进程之间共享内存)。 - nathanvda
是的,我知道。它只是说“SystemStackError stack level too deep”。 - Gaurav Shah
4个回答

2

你可以尝试使用Redis作为会话存储。我们使用rails3-redis-session-store宝石包。源代码可在此处找到。

它非常易于设置,会话会自动过期,这使得它很安全。 示例配置:

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

另一个选择是使用 dalli,从而使用 memcached 作为后端。

希望这能帮到你。


这不需要创建一个数据库本身吗? - Gaurav Shah
Redis是一种开源的键值存储系统。确实需要在服务器上运行它。你也可以使用memcached代替它。但同样,这也是一个需要在服务器上启动的进程。 - nathanvda
看起来很不错的选择……它将所有内容都保留在内存中,并在一定时间后写回磁盘,这似乎是一个好的解决方案,但仅仅为了将一个值存储在会话中有点过度杀伐。如果不行,我会尝试实现自定义内存缓存,否则就会实现你的方法。谢谢! - Gaurav Shah

1
我建议您迈出下一步,设置一个简单的数据库,这样可以为自己和用户省去很多麻烦。如果用户想要返回网站,他们将不得不重新注册。
我发现Devise非常适合此目的,并且非常容易集成。
如果有问题,您不想运行经典的数据库服务器,您可能需要查看MongoDB

没有注册。用户是通过 API 创建和维护的... Rails 应用程序不执行所有这些操作。 - Gaurav Shah

1

我将尝试分析您的选择:

如果使用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的安全性还没有信心。 - Gaurav Shah
非常抱歉,我的上一个回答中有一些错误。已经修复并添加了“客户端安全问题”部分。 - miaout17

1

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

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

如果您想在浏览器会话之外存储身份验证详细信息,则可以将它们存储在已签名的永久性 cookie 中。
cookies.permanent.signed[:user_credentials] = [login, password]

签名的 cookies 可以像普通的 cookies 一样访问:

cookies[:user_credentials]

请确保在您的初始化文件中设置一个强大的cookie_verifier_secret

ActionController::Base.cookie_verifier_secret ='dskjkjfdshfddsfkhkr3898398430943'

参考资料

Rails 3中的签名和永久性cookie


我正在尝试使用这个:https://rails.lighthouseapp.com/projects/8994/tickets/1876-uninitialized-constant-actioncontrollersessionmemorystore。但是我遇到了堆栈溢出错误。类似于这样 module ActionDispatch module Session class CustomMemoryStore < ActionDispatch::Session::AbstractStore,其余的与该网站相同。 - Gaurav Shah
@GauravShah 你是想使用有签名的cookies还是会话cookies?会话cookies默认已经被签名,所以如果你不需要在浏览器会话之外保留凭据,那么可以使用它们。 - Harish Shetty
@GauravShah 你用的是哪个版本的Rails?你在使用Cookie会话存储吗?(错误信息表明你正在使用MemoryStore。) - Harish Shetty
我理解了你关于cookieStore的观点,它几乎满足我的需求,但存在一些安全问题。因此,我想恢复旧版Rails中的MemoryStore并使用它。我正在尝试使用上面评论中的链接实现customMemoryStore,但当我使用该自定义内存存储器时,会出现stackOverFlowError错误。 - Gaurav Shah
请看一下我修改后的答案,你就会明白我想要解释什么了。@KandadaBoggu - Gaurav Shah
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接