在 TOTP 实现中,是否需要为用户的密钥制定过期策略?

信息安全 顶部
2021-09-06 00:00:25

背景

我们正在启动一个项目,为我们的 Web 应用程序添加额外的身份验证因素。这涉及创建符合 RFC 6238 ( https://www.rfc-editor.org/rfc/rfc6238 )的 TOTP(基于时间的一次性密码)令牌此令牌将通过 SMS 或电子邮件发送给用户。

我知道从安全角度来看这种方法并不理想(例如 Eurograbber、Sandroid、Emmental)。我希望引导我们的组织评估其他选项,但与此同时,我正在研究这种方法的最佳实践。

概述

  • 用户使用他们的用户名和密码对我们的 Web 应用程序进行身份验证
  • 用户启动一些触发风险评估的操作
  • 如果有风险:
    • 从数据库中提取用户的加密密钥(如果不存在,则生成新的随机密钥)
    • 使用密钥和当前时间戳生成 TOTP
    • TOTP 通过 SMS 发送到用户的手机号码(或通过电子邮件发送)
    • 显示 TOTP 输入画面
  • 用户接收并输入 TOTP
    • 从数据库中提取密钥并生成 TOTP(s)
    • 根据生成的 TOTP 验证提供的 TOTP
  • 如果有效,继续原来的行动

问题

用户的密钥是否需要有过期策略?

从逻辑上讲,密钥比用户密码(永不过期)更安全。它永远不会离开我们的系统(应用服务器和数据库)。

我可以让它过期(并生成一个新的),比如每月一次,或者每 10 次使用一次。但这似乎只有在我们的数据库(和加密密钥)被破坏时才有价值(在这种情况下,我们遇到了更大的问题)。

这里有什么最佳实践吗?

注释/假设

  • 密钥将使用 Java 的 SecureRandom 类生成(重新播种)
  • 密钥存储在数据库中时将被加密
  • 我们将确保一次性使用有效令牌。
    不接受有效令牌的第二次尝试(在同一时间窗口内)。
  • 令牌有效的时间窗口将保持尽可能小。但它需要足够大才能传递令牌(短信/电子邮件)以及用户接收和输入令牌。
    我们可能会从 90 秒开始并根据需要进行调整。
  • 小时间窗口应该防止暴力攻击?
    但是在 10 次无效令牌尝试之后添加用户锁定可能是一个好主意。
  • 用户使用有效令牌进行身份验证后,该令牌在其会话期间有效。用户以后无需重新进行身份验证。

编辑 回复:Sebastion

几周前我考虑过你的“KISS”方法,但我正计划走 TOTP 路线,因为它会为未来提供更大的灵活性。如果我们想使用 Google Authenticator(或与 RSA 或 Authy 等合作),那么我们已经成功了一半。它还稍微简化了我们的开发,因为过期是内置在算法中的。

但是......现在我退后一步,我认为你的方法可能是正确的。现在为可能永远不会发生的未来过度设计和过度复杂化是一种代码气味。

我上面没有提到的一件事是我可能会使用类似 3 30 秒的窗口。因此,在您的最后一点上,如果令牌是在窗口结束时生成的,则时间范围最多为 90 秒,或者最少为 60 秒。

1个回答

我会说:你根本不应该有密钥。当您打算通过短信或电子邮件发送这些令牌时,这意味着根本不需要密钥。

相反,我建议只是随机抽取一个令牌,将其保存在数据库中并设置一个到期时间(比如从现在起 90 秒),然后将其发送出去。每次输入令牌的尝试都应使令牌无效并要求用户有机会发送新令牌,因此每个令牌始终是“1 次尝试,成功与否”。还要添加验证码以防止有人发动随机攻击,以期“靠运气第一次命中正确的代码”。

如果您不打算让用户将种子保存到自己的设备中然后“离线”生成代码,则根本不需要使用 TOTP 协议。

但是,如果您允许用户“离线”保存他们的种子,例如配置 Google Authenticator,那么它可能是一个好主意,但有时重新配置令牌不是行业惯例。

如果您仍打算使用“种子”或“密钥”,我建议您改用 HOTP,例如基于事件的代码。因为使用 TOTP,您有时会遇到发送已过期令牌的问题,因为 TOTP 的工作方式就像一个时钟,每 X 秒“滴答”一次,在您的情况下为 90 秒。如果时钟是“85 秒”并且您将其发送出去,那么当它到达用户时它已经过期,这既增加了您不必要的成本,也增加了用户的挫败感。