Keep learning, keep living...

0%

TOTP介绍

OTPOne Time Password的缩写,是进一步加强身份认证的安全性的校验方法, 一般配合常规密码一起使用。OTP动态生成,只使用一次。手机验证码就是典型的OTP方式。用户请求一个短信验证码, 服务器随机生成一个验证码,然后临时存储起来,通过短信发送给用户。用户在系统上输入验证码发送回服务器,服务器根据临时存储的验证码进行校验。

生成OTP业内有许多标准算法,比如SHA-1。所有这些算法都包括两部分输入:

  • 种子值(seed):静态值,和帐号关联的密钥
  • 可变因子(moving factor):每次生成OTP都变化的部分

根据可变因子的不同,OTP可以分为两种类型:

  • HOTP:: H这里表示基于HMAC: Hash-based Message Authentication Code。每次HTOP被请求和校验时,服务器和客户端都增加可变因子的计数器,两端对计数器保持同步。这种方式已经由RFC 4226标准化。
  • TOTP: Time-based OTP, 这种方法的可变因子是基于时间而不是计数器。OTP的有效期一般是30秒或者60秒。如果在这个时间窗口内没有使用这个OTP,那么它就失效了,需要使用新的OTP。这种方式由RFC 6238标准化。

TOTP的算法实现非常简单, 几十行代码就可以实现,比如使用Python实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import hmac, base64, struct, hashlib, time
def get_hotp_token(secret, intervals_no):
key = base64.b32decode(secret, True)
#decoding our key
msg = struct.pack(">Q", intervals_no)
#conversions between Python values and C structs represente
h = hmac.new(key, msg, hashlib.sha1).digest()
o = o = h[19] & 15
#Generate a hash using both of these. Hashing algorithm is HMAC
h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
#unpacking
return h
def get_totp_token(secret):
#ensuring to give the same otp for 30 seconds
x =str(get_hotp_token(secret,intervals_no=int(time.time())//30))
#adding 0 in the beginning till OTP has 6 digits
while len(x)!=6:
x+='0'
return x

#base64 encoded key
secret = 'MNUGC2DBGBZQ===='
print(get_totp_token(secret))

各种语言都有相应的OTP库,Python中常用的OTP库是PyOTP, 使用非常简单,下边我们简单实践一下。

我们首先使用如下代码生成一个兼容Google Authenticator的随机密钥,我这次执行的结果是NDQARABVHZLAZGEKVNWN72IZIDKZCAK7:

1
2
import pyotp
print(pyotp.random_base32())

服务端使用如下代码生成TOTP, totp.py:

1
2
3
4
5
import pyotp

totp = pyotp.TOTP('NDQARABVHZLAZGEKVNWN72IZIDKZCAK7')

print(totp.now())

服务端使用如下代码生成OTPURI:

1
2
3
4
import pyotp

secretkey = 'NDQARABVHZLAZGEKVNWN72IZIDKZCAK7'
print(pyotp.totp.TOTP(secretkey).provisioning_uri(name='flygoast', issuer_name='just4coding'))

生成的URI如下:

1
2
(py3) [root@default src]# python uri.py
otpauth://totp/just4coding:flygoast?secret=NDQARABVHZLAZGEKVNWN72IZIDKZCAK7&issuer=just4coding

我们把这个URI转换成QR码:

现在服务端部分准备完成。客户端我们直接使用Microsoft Authenticator来进行验证。

首先打开Microsoft Authenticator扫描生成的二维码,然后可以看到当前的OTP:

在服务器上执行totp查看OTP,结果是一致的:

1
2
3
4
5
6
(py3) [root@default src]# python totp.py
775209
(py3) [root@default src]# python totp.py
775209
(py3) [root@default src]# python totp.py
775209

服务器上在收到用户发来的OTP后,可以使用如下方法进行校验:

1
totp.verify('775209') # => True

参考: