[RFC6238] TOTP: 基于时间的一次性密码生成算法

发布时间 2024-01-12 23:50:54作者: givemomo

在闲暇时间做了一个TOTP相关的开源项目,在项目初步完成之余,我尝试对[RFC6238]文档进行了翻译,供大家参考与查阅,若有不妥之处,还望各位前辈海涵斧正。

 

生活中我们会经常使用到TOTP的算法应用,如银行的动态口令器、网络游戏中的将军令、登录场景下的手机二次验证等等。

 

下图便是一个常见的OTP动态密码生成器:

图片

 

为了提高游戏账号的安全性我们在输入账号密码后,对于绑定了将军令的用户还需要输入将军令(OTP动态口令生成器)上面的一次性动态密码,验证通过后方才登陆成功。

图片

 

文章提要


 

这篇文档主要讲述了关于一次性密码(OTP)的一个扩展算法,此算法是在,RFC4226文档中定义的'基于HMAC的一次性密码算法'基础之上,支持了基于时间移动因子的扩展算法。

HOTP算法是一个以事件计数器作为移动因子,基于事件的一次性密码算法。

本文所讲述的算法则是将时间值作为移动因子。

这个基于时间的一次性密码生成算法提供了有效时间更短的一次性密码,增强了OTP算法的安全性。

此算法可以广泛的应用于互联网应用之中,包括远程虚拟专用网络(VPN)的访问控制,Wi-Fi网络登录以及面向交易的网络应用等等。

作者相信通过商用和开源的算法实现,一个通用的共享算法将会促进互联网上更多的人接触并使用到双因素身份认证算法。

概要


本文主要描述了一次性密码(OTP)的一个扩展算法,此算法是支持将时间作为移动因子的一个基于HMAC的一次性加密算法。

背景

在4226文档中描述中,HOTP算法是基于HMAC-SHA-1算法并用一个自增的计数值器来作为HMAC计算中的消息。

基本上,HMAC-SHA-1的计算输出结果都会截取为一串对用户友好的值。

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))

Truncate函数表示它能够将HMAC-SHA-1的计算结果转换成HOTP的结果值。K表示共享密钥,C表示计数值,详情请看[RFC4226]。

TOTP算法是上述算法基于时间的变体,使用通过时间戳和时间步长推导出来的数值T来代替原有HOTP算法中的计数器C。

TOTP算法的实现可以采用HMAC-SHA-256或HMAC-SHA-512函数,用此来代替原有HOTP计算中采用的HMAC-SHA-1方案。

算法要求

这一部分概括性的总结了,在设计TOTP算法时需要考虑的方面。

  1. 证明者和验证者都必须知道或者能够根据Unix时间推导得出OTP。证明者所提供的时间精度将会影响到需要多久进行一次时钟同步。

  2. 证明者和验证者必须分享相同的密钥或者密钥生成转换的方法。

  3. 算法必须使用HOTP算法来进行关键块构建。

  4. 证明者和验证者必须使用相同的时间步长X。

  5. 对于每一个证明者必须要分配唯一的密钥Key。

  6. 密钥Key应该是随机生成的或导出密钥推导算法。

  7. 密钥key可以存放在防篡改的设备之中并防止未经授权的非法访问。

TOTP算法

TOTP算法是用时间因子来表示计数器的一种计算一次性密码的HOTP算法的变体。

释义

 

  • X表示以秒为单位的时间步长(默认为30秒),它是一个系统参数。

 

  • T0是开始计步的起始Unix时间(默认是0,即)也是一个系统参数。

描述

一般而言,我们将TOTP定义为TOTP = HOTP(K, T),其中T是一个整数,代表了在初始时间T0和当前Unix时间之间的时间步长。

更具体的说,T = (Current Unix time - T0) / X,默认在计算时向下取整。

比如,T0=0,X=30时,当前Unix时间为59秒那么T=1,当前Unix时间为60秒时,那么T=2。

算法的实现必须支持,当时间T超过2038年后,时间数值大于32位整数这种情况。系统参数X和T0是预先就确定好的了,作为准备步骤的一部分,已经在客户端和服务端进行了同步。相关的同步流程已超出了本文档的表述范围,详情请见[RFC6030]。

基于安全的考量

常规的

算法的安全性和可靠程度依赖于用于构建HOTP块的属性参数。它是在构造HMAC时使用的SHA-1作为哈希函数。

通过[RFC4226]中详尽的安全性分析结论得知,在所有真实场景下,针对不同的输入,所得到的输出结果截断,都是相互独立且没有必然联系的字符串。

分析表明,针对HOTP最有可能的破解方式就是暴力破解。

如算法实现中要求的那样,密钥key应当选择随机值或者通过设置了合理随机种子安全的强伪随机数生成器生成随机数。

密钥的长度应当与HMAC的输出长度一致,已达到复用。

对于伪随机数以及随机数生成器相关的事项,建议遵从[RFC4086]的规则定义。用于生成密钥的伪随机数要求能够通过在[CN]中指定的随机性测验或者其他公认的测试。

所有通信应该建立在安全通道之上,例如安全套接字层/传输层安全或IPsec连接。

我们也同样建议将密钥安全地存放在认证系统之中,更加具体的讲,使用防篡改的硬件来加密客户端的密钥。仅当,真的有必要时才将其暴露出来,比如在认证一次性密钥是否正确是,需要对密钥进行解密,当此操作完成之后便立即重新加密,以此来最大限度地缩短密钥明文变量暴露在内存中时间。

密钥必须存放在安全可靠的地方,以尽可能的避免对认证服务器系统和存放密钥的数据库的直接攻击。
特别是,获取密钥信息这一操作应当被限制在验证系统所必须的程序或相关执行过程之中。

验证和时间步长

在相同的时间步数内生成的一次性密码结果是一样的。当验证服务器接收到一个一次性密码时,它并不知道客户端具体是在何时生成的这个一次性密码。验证服务器或许会使用接收到客户端密码的这个时间来生成密码并与客户端密码进行比较。

但由于网络延迟,客户端生成出一次性密码的时间和服务端接收到的时间差距(用T进行计量,它是从T0开始的时间时间步数)可能会很大。验证服务器接收到的时间和实际生成一次性密码的时间有可能不会落在同一个时间计步窗口下,由此可能会产生两个不一致的一次性密码。

当客户端生成一次性密码的时间在时间计步窗口靠后的位置,那么服务端收到密码的时间就很有可能会落到下一个计步窗口之中。验证服务器通常要为因传输延迟而不能匹配有效的一次性密码的情形设置相应的延时窗口来进行验证。

这样的话,认证系统就不应当用接收到一次性密码的时间来生成比较密码了,还需要将在传输时延内的那些密码也要包含进去进行验证。越大的可接受时延窗口会暴露出更大的攻击窗口。所以我们建议最多使用一个时间步长来应对网络时延问题。

时间步长影响着系统的安全性和可用性。一个更大的时间步长意味着认证系统要设置更大的一次性密码的可接受窗口。

使用更大的时间步长会关联到以下几个方面:

首先,更大的时间步长会暴露出更大的可攻击窗口。当一次性密码生成完成并在失效之前暴露给第三方,那么第三方就能够在这个时间窗口内继续使用这个一次性密码。

我们建议默认的时间步长为30秒。将其设置为30秒是综合考虑了安全性和可用性的结果。

第二,下一个不同的一次性密码只能在下一个时间窗口下才能生成。一个用户必须等到时钟到了下一个时间窗口内才能进行新的提交。这个等待时间不一定等于时间步长,它依赖于最后一次生成一次性密码的时间。

比如,如果最后一次生成一次性密码的时间是在时间窗口的一半的位置,那么等待生成下一个一次性密码的时间就会是步长的一半。一般而言,越大的时间步长意味着用户需在上一个成功的一次性密码验证之后,需要等待更长的时间才能够生成下一个合法的一次性密码来进行新的验证。

一个超大的窗口,比如十分钟,很可能不适合典型的互联网用户的登录需求。用户无法在十分钟之内获取到下一个一次性密码,所以就让他等十分钟再来登陆吗?

注意,认证者可以在相同的时间窗口内多次发送相同的一次性密码到服务器上来进行认证。所以在同一个时间窗口下服务器在第一个一次性密码验证成功之后就不能在接受验证第二个一次性密码了,这也确保了一个时间内只使用一个一次性密码(否则一个30秒的时间窗口,暴力轮接口肯定能把6位密码给攻破)。

同步校准机制

由于可能存在客户端时钟和认证服务器的时钟不同步的问题,我们建议验证者在拒绝掉客户端'错误的'认证之前,通过设置步长的方式对不同步的时间进行校准。

这个限制可以是,从收到OTP值时的计算时间开始,向前和向后设置以步长为单位的时间,如果时间步长设置为30秒,验证者要向后设置两个时间步长,如果这样的话最大的漂移时间就会在89秒左右(允许客户端与服务器有89秒的误差),即用于计算的那个时间步长内剩余的29秒和向后的两个时间步长的60秒。

这意味着验证器会用当前时间窗口执行验证,也会用由此向后的两个时间窗口再进行验证(一共是三个时间窗口的验证)。认证成功之后,认证服务器可以记录所侦测到的时钟漂移。在此步骤之后,当收到一个新的一次性密码时,认证者可以通过当前时间和之前记录的时钟漂移步长得出的一个校准后的时间来验证客户端的一次性密码。

同样,需要注意的是,客户端未向服务器发送一次性密码的时间越长,由此积累的时钟漂移就有可能会越来越大。在这样的场景下,如果漂移量大于程序所允许的阈值的话,那么自动校准功能可能就会失效。这时就应该使用其他的认证方式进行安全身份验证,并强制校准客户端与服务端的时钟,使二者保持同步。

 

作者:给我馍馍
博客:https://www.cnblogs.com/givemomo/

 

本篇文章如有帮助到您,请给「给我馍馍」点个赞,感谢您的支持。