在内存中缓存 bcrypt 检查是否安全甚至常见

信息安全 密码 哈希
2021-09-02 17:55:57

在(作为实现密码反模式的副作用)存储密码的 Web 应用程序中,一个建议是使用 bcrypt 来存储它们(或计算昂贵的单向加盐哈希)。但是,这样做会使服务器进程将大部分时间花在 CPU 密集型 bcrypt 比较上,因为使用 bcrypt 的全部意义在于测试密码需要时间。

我的问题是:缓存(在内存中)bcrypt 比较的响应是否安全?我不担心每个进程花费一次时间,但每个请求一次将成为可伸缩性瓶颈。加分(?)如果您可以在通常的做法中有所启发。

我考虑过的事情:

  • 如果它仅在内存中,则更难攻击
  • 如果永久存储被破坏,它会像以前一样安全
  • 降低 bcrypt 负载因子会使密码数据库更容易受到攻击(如果受到攻击)
3个回答

密码散列用于长期密码存储,以处理攻击者不需要的只读数据库访问的情况。这是一个现实的威胁;这种读取访问是成功的 SQL 注入攻击的常见结果。

另一方面,一种常见的模型是假设攻击者无法读取实时服务器的 RAM 内容。在该模型中,将密码本身缓存在 RAM 中没有问题,这完全避免了访问数据库:密码验证只是内存中的比较。攻击者在无法完全控制机器的情况下读取 RAM 的场景非常复杂,不太可能适用。

缓存 bcrypt输出本身并没有多大意义,因为要使此信息有用,它必须以某种方式由密码和盐索引(在内部,将有一个映射“密码+盐”到“bcrypt 输出” ),因此可以从 RAM 读取 bcrypt 输出的攻击者应该能够读取明文密码本身。尽管如此,在给定的现有服务器实现中,将 RAM 缓存改造成这样的映射可能更容易(在代码中,您可以透明地将其插入“bcrypt 引擎”);如上所述,缓存 bcrypt 输出并不比在 RAM 中存储明文密码差,这通常很好。

从您的问题来看,听起来您在每次请求时都将密码发送到服务器。如果是这样,我不得不问:你到底为什么要这样做?

通常的模式是在“登录”请求时发送一​​次密码。如果密码正确,服务器会向客户端发送一个随机生成的临时身份验证令牌。这个令牌应该足够长,即使没有关键的拉伸技术也能承受蛮力攻击,并且有足够短的有效期,破坏它只会带来有限的风险。(对于特别安全的操作,比如更改长期密码,要求客户端重新发送原始密码是合理的。)

通常,此类令牌以简单的形式存储在服务器端的某些短期会话存储中。我想您可以使用一些非拉伸散列函数对其进行散列,以便令牌存储的妥协不会自动危及所有活动会话。

在客户端,Web 应用程序通常将令牌存储在 HTTP cookie 中,以便在每次请求时自动将其发送到服务器。请注意,如果您这样做,您应该注意保护您的应用免受跨站点请求伪造攻击。

  • 不,这对于密码哈希来说并不常见
  • 这并不常见,因为您真正想做的是在登录后分配一个临时会话 ID,而不是不断地验证密码。如果以某种方式实施,这也有助于防止 CSRF 攻击。
  • 如果修复您的架构需要大量工作,您可以将密码 <-> bcrypt 结果缓存一段时间作为权宜之计。确保它仅存储在内存中并在短时间内过期。10分钟可能是合适的。