背景
我们正在启动一个项目,为我们的 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 秒。