MFA与OTP揭秘:你每天都在用却不知道的安全认证,秒懂双重验证原理 🔒 HOTP TOTP机制

这篇博客介绍下MFA和OTP,这两个概念大家日常或多或少都接触过,但不一定知道叫啥名字。看完你会了解什么是MFA,为什么需要MFA,什么又是OTP,为什么要OTP,以及两种常见OTP的实现机制。

MFA

什么是MFA,他是Multi-Factor Authentication的缩写,中文翻译为多因素认证。所谓的认证,就是证明你是你!在网络世界里,通常通过密码来证明自己的身份。而密码就是其中一种因素,MFA要求用户同时使用多个因素来验证身份,如密码、短信验证码、人脸识别等,来提高安全性。

每增加一个因素,就如同增加了一把锁,安全性自然会提高,同时也会带来很多的麻烦。比如开发运营成本会增加,更重要的是用户体验也会变差。因此需要做个权衡,并非越多因素越好,正因为如此,我们经常会听到2FA,也就是双因素认证,普遍认为两个因素能在安全性和用户体验上都获得比较好的效果。我们经常碰到的MFA实现是密码+OTP。尤其是在一些企业里面,许多关键系统除了需要密码外,还需要额外输入一个令牌(token),那个令牌就是OTP。

OTP

那么什么是OTP,OTP(One Time Password)即一次性密码,也是一种认证方式。它通过一个算法生成一个动态的密码,用户只需要在登录时输入这个密码即可完成认证。

顾名思义,这种密码是一次性的,用过后就无效了,也就不用再担心泄露了。因此他是比较有效的防止重放攻击的手段。OTP的另一个特点是时效性,即使没有使用,过了一定的时间也会自动失效,或者30秒或者5分钟后就会失效,从而可以防止暴力破解。

一次性密码确实挺好,但是有个问题,这个密码一直在变,认证双方如何同步呢?最简单的一个方法是认证方告诉被认证方当前的密码,那怎么样告诉被认证方呢?我们可以通过其他渠道通知被认证方,比如短信、邮件、电话等,这就是我们经常碰到的短信验证码,邮件验证码,电话验证码等。

而短信、邮件、电话有可能被劫持,导致密码泄露,那既然这么说了,肯定有更好的办法了。

HOTP和TOTP

这就是HOTP和TOTP。HOTP和TOTP都是一次性密码的实现方式。这两种方式跟前面提到的短信验证码,邮件验证码的区别在于,这两种方式提供了一个机制,使得认证方和被认证方不需要每次都要通知对方。接下来我们看看,这两种机制是如何实现不需要通知对方,还能保持同步的。

HOTP是HMAC-based One Time Password,基于HMAC的一次性密码,也有些地方说是基于计数的一次性密码,其实都没错。生成公式是:

HOTP = HMAC-SHA1(K, C)[截断]

这里有两个关键要素用于生成HOTP一次性密码,第一个是密钥K,这是认证双方事先约定好的,需要安全保管,一旦泄露,由其生成的一次性密码也就泄露了;第二个是计数C,这是为什么HOTP也叫基于计数的一次性密码,通过递增计数来生成跟上次不一样的密码。然后利用哈希函数SHA-1,将K和C进行运算,得到一个20位的二进制数,最后通过截断算法,将这个20位的二进制数截取为6位,即为HOTP的密码。关于截断算法,如果大家有兴趣,我们可以另外出一个视频来介绍。

认证的过程中,双方都持有密钥K,并从计数0开始,各自生成一个6位密码,如果一致则认证通过,否则认证失败。

所以聪明的各位应该已经意识到,这里的关键是如何保证认证双方的计数C是同步的。因为如果计数不同步,双方各自生成的密码就会不同,从而导致认证失败。

在实际应用中,计数C不同步的情况非常常见,所以认证方往往会依次验证C、C+1、C+2、C+3、C+4对应的密码,只要有一个密码正确,则认证通过。这一定程度上降低了认证失败的概率,但并没有完全解决这个问题。

由此,引出了TOTP(Time-based One Time Password),我们知道正常情况下我们的时钟是始终保持同步的,那么基于时钟的计数自然也是始终保持同步的。

我们继续使用HOTP的公式,不同的是计数C来自于时间,当前时间减去一个起始时间,这个起始时间就是我们设定的,一般我们设定为0,也就是从1970年1月1日0时0分0秒开始。

C = (当前时间 - T0) / 时间窗

这样只要我们的设备获取到正确的时间,就可以计算出正确的密码。从而解决了认证双方计数不同步的问题。公式中的时间窗默认是30秒,也就是每30秒生成一个新的密码。当然认证双方也可以约定其他的时间窗,比如60秒、90秒等。

这里也有个特殊情形,就是被认证方生成的密码是基于上一个时间窗,但是认证方收到密码,开始验证已经是下个时间窗,为了能很好的处理这种场景,认证方往往会同时结算上个时间窗的密码,只要有一个匹配上了,就认为成功。

应用示例

我们经常碰到的MFA,通常是TOTP,比如GitHub、Google、Facebook等。初次设置MFA的时候,网站会提供一个二维码,我们通过手机App微软的Authenticator或者Google Authenticator扫描二维码,然后手机App会生成一个动态密码,这个动态密码可用于跟网站做认证,从而实现MFA。

而这个二维码包含了用于生成TOTP的关键要素,密钥K,时间窗大小,以及一次性密码的长度,默认长度为6位。

实现

在Python中,我们可以使用Python库pyotp来生成一次性密码。以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
import pyotp
import base64

secret = "JBSWY3DPEHPK3PXP"

totp = pyotp.TOTP(base64.b32encode(secret.encode()))
hotp = pyotp.HOTP(base64.b32encode(secret.encode()))

print("Your TOTP code is:", totp.now()) # 生成TOTP

print("Your HOTP code at [1] is:", hotp.at(1)) # 生成HOTP