设备的token_authenticatable安全吗?

80
我正在使用Rails API构建一个简单的api,并希望确保我在正确的轨道上。我正在使用devise来处理登录,并决定使用Devise的token_authenticatable选项,它会生成一个API密钥,您需要在每个请求中发送该密钥。
我将API与backbone/marionette前端配对,通常想知道如何处理会话。我的第一个想法是只需将API密钥存储在本地存储或cookie中,并在页面加载时检索它,但从安全角度来看,以这种方式存储API密钥似乎有些不妥。通过查看本地存储/ cookie或嗅探任何经过的请求,是否很容易获取API密钥并用它无限期模拟该用户?我目前在每次登录时重置API密钥,但即使如此,这似乎也太频繁了 - 每当您在任何设备上登录时,这意味着您将在其他所有设备上注销,这有点令人烦恼。如果我可以去掉这个重置,我觉得从可用性的角度来看会改善很多。
我可能完全错了(并希望我错了),是否有人可以解释一下以这种方式进行身份验证是否可靠安全,如果不是,则有什么好的替代方案?总体而言,我正在寻找一种方法,可以通过安全方式让用户“签入”API访问而无需频繁强制重新身份验证。
2个回答

195
token_authenticatable容易受到时间攻击,这在这篇博客文章中有很好的解释。这些攻击是token_authenticatable从Devise 3.1中删除的原因。有关更多信息,请参见plataformatec博客文章
为了拥有最安全的令牌身份验证机制,令牌应:
  1. 通过HTTPS发送。

  2. 必须是随机的,具有密码学强度。

  3. 必须进行安全比较。

  4. 不能直接存储在数据库中。只能在那里存储令牌的哈希值。(记住,令牌=密码。我们不会以明文形式在数据库中存储密码,对吧?)

  5. 应根据某些逻辑过期。

如果您为了可用性而放弃了其中的一些要点,那么最终得到的机制就不会像可能的那样安全。就是这么简单。如果您满足前三个要求并限制对数据库的访问,则应该足够安全。
扩展和解释我的答案:
  1. 使用HTTPS。这是最重要的一点,因为它涉及到窃听者。

    如果你不使用HTTPS,则可能出现很多问题。例如:

    • 要安全地传输用户凭据(用户名/电子邮件/密码),你必须使用摘要认证,但在当今的情况下,盐值哈希可以被暴力破解

    • 在Rails 3中,Cookie仅由Base64编码保护,因此可以相对容易地被揭示。有关更多信息,请参阅解码Rails会话Cookie

      尽管如此,在Rails 4中,Cookie存储已加密,因此数据既经过数字验证也无法被攻击者读取。只要你的secret_key_base没有泄露,Cookie就应该是安全的。

  2. 使用以下命令生成令牌:

    关于为什么需要这样做的详细说明,请参阅sysrandom的README和博客文章各种编程语言生成安全随机数的方法

  3. 使用用户ID、电子邮件或其他属性查找用户记录,然后使用Devise.secure_compare(user.auth_token, params[:auth_token]将该用户的令牌与请求的令牌进行比较。

    如果你使用的是Rails 4.2.1+,也可以使用ActiveSupport::SecurityUtils.secure_compare

    不要使用类似于User.find_by(auth_token: params[:auth_token])这样的Rails查找器查找用户记录。这容易受到时间攻击的影响!

  4. 如果你要同时为一个用户拥有多个应用程序/会话,则有两个选项:

    • 在数据库中存储未加密的令牌,以便它可以在设备之间共享。这是一种不好的做法,但我猜你可以出于UX的考虑这样做(如果你信任你的员工可以访问数据库)。

    • 为每个允许当前会话的设备存储尽可能多的加密令牌。因此,如果你想在2个不同的设备上允许2个会话,则在数据库中保留2个不同的令牌哈希。这个选项实现起来可能不太直观,但绝对更安全。它还有一个好处,就是允许你为用户提供在特定设备上结束当前活动会话的选项,方法是撤销其令牌(就像GitHub和Facebook一样)。

  5. 应该有某种机制使令牌过期。在实现这个机制时考虑到UX和安全之间的权衡。

    Google会在六个月内未使用令牌时使其过期

    Facebook会在两个月内未使用令牌时使其过期

    使用Facebook SDK的本地移动应用程序将获得长期访问 令牌,有效期约为60天。当使用你的应用程序的人向Facebook的 服务器发出请求时,这些令牌每天都会刷新一次。如果没有发出请求,令牌将在大约60天后过期,此时用户必须再次进行登录以获取新的令牌。

  6. 升级到Rails 4以使用其加密Cookie存储。如果无法这样做,则像这里建议的那样自己加密Cookie存储。在加密Cookie存储中存储身份验证令牌绝对不会有问题。

你还应该有一个备用计划,例如,一个耙子任务来重置数据库中的一部分令牌或每个令牌。
为了帮助您入门,您可以查看这个gist(由Devise的作者之一)关于如何使用Devise实现令牌身份验证。最后,Railscast on securing an API应该会有所帮助。

太棒了,这非常有帮助 - 谢谢!这几乎肯定会得到正确的答案。如果您在此基础上添加您的意见/建议(具体而言),以处理API身份验证的最佳方式,奖金就是您的:) - Jeff Escalante
1
虽然两种方法都可以生成随机字符串,但 urlsafe_base64 会生成一个安全的 URL 字符串。这就是名字的由来。除非你想在 URL 中使用令牌(不建议这样做),否则请使用 hex - Ashitaka
2
令牌(token)不等于密码。将令牌以明文形式存储并没有问题。以明文形式存储密码的问题在于密码可能会被用于其他地方,而这种情况不应该出现在您的令牌中。 - fatfrog
2
@fatfrog 不是令牌就是密码。如果黑客或不满意的员工可以访问您的数据库,他不应该能够作为某个用户或管理员进行身份验证。 - Ashitaka
1
我不同意,如果黑客或不满的员工可以访问您的数据库,那么令牌是您需要担心的最后一件事情。他们已经拥有了您的数据。 - fatfrog
显示剩余6条评论

3
你可以尝试使用rails4来构建API,它提供了更多的安全性,并使用devise 3.1.0rc
在Rails 4.0中,几个功能已经被提取到宝石里。
- ActiveRecord::SessionStore - 动作缓存 - 页面缓存 - 基于密钥到期的俄罗斯套娃缓存,自动管理嵌套模板的依赖关系。http://blog.envylabs.com/post/41711428227/rails-4-security-for-session-cookies Devise 3.1.0.rc可以在Rails 3.2和Rails 4.0上运行。http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/ Devise在3.1.0rc版本中弃用了TokenAuthenticatable,但是您可以构建自己的TokenAuthenticatable方法来解决安全问题。它更可靠和安全。

关于令牌和会话存储,您可以查看 http://ruby.railstutorial.org/chapters/sign-in-sign-outhttp://blog.bigbinary.com/2013/03/19/cookies-on-rails.html 以获取更易理解的信息。

最后,您应该阅读这些加密和解密相关内容 "Unable to decrypt stored encrypted data" 以获得更高的安全性。


有没有人有一个自定义构建的替代方案的例子? - Daniel Morris

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