首先,我知道,不要重新发明轮子.但这是有充分理由的。
因此,我一直在实现一个通用的2要素身份验证代码生成/验证算法,以便在多个应用程序中重用它,并使它成为无状态的(该算法不需要在数据库中存储任何内容,而且它根本不需要任何用户数据)。
使用代码的应用程序负责在通用的2fa进程中设置有效负载并对其进行验证。
这就是它的工作原理..。
1-应用程序请求传递有效载荷的新的2FA代码:
1.1 -有效载荷:任意数据,如用户id和应用程序id。
1.2 -创建一个随机7位代码(使用加密安全的随机库)
1.3 -选择一个过期时间,即从当前时间起3分钟,以秒为单位表示为划时代时间。
1.4 -数据:(到期、有效载荷、代码)
1.5 -使用AES CTR对数据进行加密,随机生成的IV为128位,密钥为
1.6 -加密数据和IV都通过使用另一个秘密密钥的HMAC进行解析,以便对这两个值进行签名。
1.7 -最后令牌:'base64加密数据: IV :签名‘
1.8 -令牌返回给用户UI (例如移动应用程序或登录webiste)
1.9 -代码通过短信或电子邮件发送。
2-应用程序提交原始令牌,用户提供的代码到后端。
2.1 -令牌被解码,签名被验证。
2.2 -令牌被解密,过期检查,用户代码与令牌代码进行比较
2.3 -有效载荷返回给应用程序,该应用程序执行任何额外的验证,例如从有效负载中的用户id中获取用户,等等.
这个坏了吗?您可能会问,为什么不只是将7位代码存储在数据库中,而是为了使其完全无状态和通用。
还要注意:用于令牌生成/验证的端点应该以一种不能强行强制的方式进行节流。
发布于 2018-07-05 21:44:46
在考虑了15分钟之后,假设它的实现是正确的,并且应用程序正确地使用了它,我唯一能想到的小问题就是语言问题:
1.8 -令牌返回给用户UI (例如移动应用程序或登录网站)。
UI意味着它是呈现给用户的。用户不需要查看令牌,应该将其返回给用户客户端,用户客户端不需要用户查看就可以提交令牌。这当然是一个非常小的问题,因为它并没有真正对用户造成任何伤害,它只是没有必要。
当然,你可能需要一个比我更好的人来思考这件事超过15分钟。
就我个人而言,一旦有足够多的人使用2FA (足够多的位置支持2FA),钓鱼者就会开始使用像埃维尔吉斯这样的代理,而OTP解决方案很容易受到这种代理的影响。据我所知,目前唯一阻止这种情况的2FA方法是U2F,它需要为每个用户存储一个公钥。
我认为,如果不为每个用户存储公钥、秘密或类似的东西,就不可能防止这种类型的网络钓鱼。如果我正在实现2FA,我将尝试使其与U2F兼容,或者至少尝试设计它,以便在需要时可以轻松地添加U2F或未来的方法。
还请注意,短信和电子邮件都没有强有力的保证,他们不会被拦截。至少对于TOPT或HOTP,您可以使用像Google身份验证器这样的工具,它虽然仍然容易受到恶意代理的攻击,但至少不必发送可以被拦截的代码。
发布于 2018-07-05 20:30:28
看上去像是“我不想走一公里,所以我要走一百公里”。将七个数字保存在数据库、平面文件或任何东西中都要简单得多,而且也是通用的。您的解决方案不是无状态的,因为状态是发送给客户端的。
我绝对不会使用你的系统,它充满了移动部件。如果你要通过电子邮件或短信发送一个令牌给用户,只需保存它,然后进行比较。一个小型的sqlite数据库就足够了,而且几乎在每种语言中都有使用它的库。您将拥有这样的数据库:
[userid | token | expiration ]
|192 | 1234567 | 2018-07:15 10:11:12|
|723 | 7482912 | 2018-07:15 15:24:33|
----------------------------------------为了防止数据库变得太大,您可以每隔一个小时运行一个作业来删除过期的令牌。
您可以稍微简化系统,并通过使用适当的TOTP来摆脱清理工作。使用userid和键创建一个数据库。当用户想要进行身份验证时,您将请求令牌。用户将打开Google、奥西、1个通、恩帕斯或任何他使用的TOTP客户端。当用户提交令牌时,可以计算和验证令牌。几乎每种语言都有很多TOTP库可用。
要使用第二种方法,您将只向应用程序添加一个非常小的sqlite数据库和一个库。我认为开销非常小,并且大大简化了您的过程。
https://security.stackexchange.com/questions/189029
复制相似问题