一次性密码

2018-05-30
Algorithm

一次性密码,(One Time Password,简称 OTP),又称动态密码或单次有效密码。常见的二次验证程序,比如 Google Authenticator 使用的就是 OTPOTP 分两种,一种是基于时间的一次性密码,(Time-based One-Time Password,简称 TOTP;另一种是基于记次的一次性密码,(HMAC-based One-Time Password,简称 HOTP)。

OTP

OTP 的核心在于:客户端与服务端在配对时,协商好算法、密钥和可变数,这个可变数可以是基于时间的或基于次数的,下面会说到。在算法、密钥、可变数都一致的情况下,客户端与服务端会产生一致的一次性密码。

OTP 的好处在于,它是可变的,不像常规密码是不变的。这个特性可以有效避免重放攻击和暴力破解,也能避免用户在不同系统使用同一密码带来的不安全性。

TOTP

TOTP 的标准是 RFC6238,主要由以下几个参数构成:

加密哈希算法

默认使用 sha-1 作为加密哈希算法,也可以为 sha-256sha-512 等。

T0

T0 为开始时间戳,默认为 UNIX 时间戳 的开始,即 1970 年 1 月 1 日 0 时 0 分 0 秒。

TS

TS 为密码变化的时间间隔,默认是 30 秒,也可以为 60 秒等。

TC

TCT0 到现在的 TS 数量,即 Math.floor((NOW - T0) / TS)

密钥 K

密钥对于每个用户应该是不同的。

密码长度

因为一次性密码是用来给用户输入的,最终生成的密码长度不宜过长,默认是 6 位数字,也可以是 8 位等。

一次性密码 = 加密哈希算法(K, TC)

客户端与服务端配对时,协商好这些参数,就能各自生成一次性密码了。不过这里还有两个问题:

  1. 时间同步的问题。客户端与服务端可能时间不同步,或者因为网络原因导致时间不一致,当时间差值大于 TS 时,双方产生的一次性密码就不一致。解决方法是:服务端应该尝试 TC = TC +- W,以兼容时间超前或延后的问题。也就是说,假如 TC10W5 时,那 TC5 ~ 15 的范围内产生的密码,服务端都会接受,并且在密码验证成功后,服务端应设置一个 offset 偏量,以同步时间。当然这个 W 参数不宜过大,这样会不安全;也不宜过小,这样解决不了时间不同步的问题。

  2. 暴力破解的问题。因为一次性密码为了方便用户输入,一般只取 6 位数字,那 30 秒内其实只有 1000000 种可能值,这很容易被暴力破解。服务端应该设置一个最大尝试次数来避免暴力破解,比如在 30 秒内,只让尝试 5 次,这样就比较安全了。

HOTP

了解 TOTP 之后,HOTP 就比较好理解了。

HOTP 的标准是 RFC4266,主要参数有:加密哈希算法、密钥 K,计数器 C。

一次性密码 = 加密哈希算法(K, C)

不像 TOTP 基于时间,时间会自动流逝,HOTP 基于次数是要双方共同维护的。客户端每生成一个密码,计数器加一;服务端每验证一个密码,计数器加一。

HOTP 同样有两个问题:

  1. 计数器同步的问题。由于客户端请求生成密码后,不一定会用来认证,这样客户端的计数器很可能超前于服务端。为了解决这个问题,服务端应尝试 C = C + W 来兼容这个问题。比如当前服务端计数器 C10W5 时,C10 ~ 15 范围内生成的密码服务端都应该接受,并且验证成功后需要同步计数器。当然如果客户端生成密码的按钮被小孩子发现,按了几十上百次,那就只能一首凉凉送给你了 :)。

  2. 暴力破解的问题。

OTP 的应用

二次验证码是 OTP 最普遍的运用,这里要说一下二次验证的典型:谷歌身份验证器(Google Authenticator)。

OTP 客户端与服务端协商参数的交互过程是很麻烦的,如果要用户把参数一个个输入来配对,那估计没人想用了。

于是 Google Authenticator 定义了一个 URI schemm 来解决这个问题,形如:

otpauth://TYPE/LABEL?PARAMETERS

其中:

TYPE:可以是 totphotp

LABEL: 用来指明用户,可以是一个账户,比如 user@example.com,或者前缀加上账户,比如 Example:user@example.com

PARAMETERS: 其它参数

secret:必须,base32编码的密钥。
issuer: 推荐,跟账户前缀一样,用来解决多个人用同一个账户的问题。
algorithm: 可选,哈希算法,默认是 SHA1,还可以是 SHA256 、SHA512。
digits: 可选,密码长度,默认是 6。
counter: TYPE hotp 时必须指定,计数器。
period: TYPE totp 时可选,时间周期,默认 30 秒。

uri 中特殊字符应该 url encode 一下,完整的 uri 形如:

otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example

otpauth://hotp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example&counter=1

光有 URI schemm 还不够,要让用户输入这么长的字符还是很困难的,生成一个二维码就好很多了,扫一扫就能配对。

示例二维码:

qrcode

总结

介绍了一下 TOTP 与 HOTP,以及 Google Authenticator 的原理。没有很具体地讲算法,想了解的话可以查看相关代码实现。

参考:

(完)

This post was published 1791 days ago, some content may be inaccurate. Edit it on GitHub
Comment loading