基于GoogleAuthenticator的MFA多重身份认证
关于
对于一个需要身份认证的系统而言,账号和密码是必备的,而敏感系数比较高的应用,固定的账号和密码不足以保护其安全性。通常会采集多种信息来确认当前访问者的身份,即多因素认证(Multi-factor authentication,MFA),常见的多因素认证方式:
- 短信验证码
- 邮件验证码
上面介绍的几种方式在成本,可用性,便捷性上来说都表现不是很好,比如短信和邮件都需要收费,同时都必须保证网络在线。
Google现在也推荐用户启用两步验证(2 Factor Authentication)功能,并且除了以短信或者电话的方式发送一次性密码之外,还提供了另一种基于时间的一次性密码(Time-based One-time Password,简称TOTP),只需要在手机上安装密码生成应用程序,就可以生成一个随着时间变化的一次性密码,用于帐户验证,而且这个应用程序不需要连接网络即可工作。Google的TOTP方案其实是基于HOTP(HMAC-based One-Time Password),所以在介绍TOTP之前先了解一下HOTP的原理。
HOTP
HOTP是一种基于散列消息验证码(HMAC)生成一次性密码值的算法,HMAC利用哈希算法,以一个密钥和一个消 息为输入,生成一个消息摘要作为输出。
进行验证时,客户端对密钥和计数器的组合(K,C)使用HMAC(Hash-based Message Authentication Code)算法计算一次性密码,公式如下:
1 | HOTP(K,C) = Truncate(HMAC-SHA-1(K,C)) |
- K为双方协定的密钥,需要自行保存,不要泄漏
1 | otpauth://totp/op_app:AccountName?algorithm=SHA1&digits=6&issuer=op_app&period=30&secret=SecretKey |
- C为计数器,每次认证通过都会+1
- 上面采用了HMAC-SHA-1,当然也可以使用HMAC-MD5等
- HMAC算法得出的值位数比较多,不方便用户输入,因此需要截断(Truncate)成为一组不太长十进制数(例如6位)。
TOTP
表示基于时间戳算法的一次性密码。 即基于客户端的动态口令和动态口令验证服务器的时间进行比对,一般每30秒产生一个新口令,要求客户端和服务器能够十分精确的保持正确的时钟,客户端和服务端基于时间计算的动态口令才能一致。
1 | TOTP = HMAC-SHA-1(K, (T - T0) / X) |
- T0是约定的起始时间点的时间戳,默认是0,也就是1970年1月1日 00:00:00。T 是当前时间,X为时间步长,通常为30s。所以(T - T0) / X指的是当前到1970年1月1日 00:00:00间隔多少个30s
- K为双方协定的密钥
- HMAC-SHA-1是约定的哈希函数
验证码计算
RFC4226标准: https://datatracker.ietf.org/doc/html/rfc4226#section-5.4
1 | func GenerateCode(secret string, counter uint64, opts ValidateOpts) (passcode string, err error) { |
容错机制
- 由于网络延时,用户输入延迟等因素,可能当服务器端接收到一次性密码时,T的数值已经改变,这样就会导致服务器计算的一次性密码值与用户输入的不同,验证失败。解决这个问题个一个方法是,服务器计算当前时间片以及前面的n个时间片内的一次性密码值,只要其中有一个与用户输入的密码相同,则验证通过。当然,n不能太大,否则会降低安全性。
1 | counters := []uint64{} |