安当OTP:一文读懂 TOTP 时间同步动态口令的工作原理
一、为什么我们需要"一次性密码"
在讲原理之前,先聊聊痛点。绝大多数系统的登录认证,至今仍停留在"账号 + 静态密码"这一层。静态密码有几个绕不开的毛病:
第一,会被窃取。键盘记录器、钓鱼网站、撞库攻击、数据泄露,都能让密码以明文形式落在不法分子手里。Verizon《数据泄露调查报告》常年显示,八成以上的入侵与凭据失窃相关,内部入侵与凭据暴力破解合计占比超过八成。也就是说,你的系统在"外人用偷来的密码登录"和"内鬼用自己知道的密码乱来"这两个场景下,几乎毫无招架之力。
第二,会被复用。人记不住几十个复杂密码,于是同一个密码用遍所有网站。一个网站泄露,所有账户裸奔。
第三,会被穷举。弱密码在 GPU 集群面前几秒就被猜光。即便强制复杂度策略,用户也会写在便利贴上贴在显示器边。
这些问题的共同根源是:密码是一个长期不变、可重复使用的秘密。攻击者只要拿到一次,就能反复用。
解决思路很朴素——让"密码"每次都不同,用过即废。这就是OTP(One-Time Password,一次性密码)的核心思想。而 OTP 里最主流、部署最广的实现,就是TOTP(Time-based One-Time Password,基于时间的一次性密码)。安当 OTP 的手机令牌与服务端,正是构建在 OATH TOTP 标准之上。
本文就带你从零推导 TOTP 的数学原理,搞清楚"为什么一个 6 位数字能挡住攻击者"。
二、TOTP 的整体框架:客户端与服务端的"时间约定"
TOTP 不是凭空变出一个数字,它的本质是一个客户端与服务端共享秘密,并基于时间同步分别独立计算的协议。
整套系统由两部分组成:
- 客户端(动态令牌):可以是安当令牌 APP(Android/iOS),也可以是硬件令牌。它持有密钥,并随当前时间生成一个动态码。
- 服务端(认证系统,如安当 ASP 身份认证服务):持有同一份密钥,并在用户登录时校验客户端提交的那串动态码是否一致。
关键事实:密钥从始至终只在客户端和服务端各自保存,动态口令的生成与验证过程中,密钥本身从不通过网络传输。网络上只跑"此刻的动态码"这一串会过期的数字,就算被中间人截获,三十秒后它也失效了。这一点是 OTP 安全性的基石,后面会反复强调。
那么,双方怎么做到"不说话也能算出同一个数"?答案是:约定一个相同的密钥和相同的时间起点,然后各自用"当前时间"这个谁都能看到的公开变量去算。
三、时间计数器:把"现在"变成一个整数
TOTP 的第一步,是把时间量化成离散的"格"。
设:
T0为时间起点(Unix 纪元,1970-01-01 00:00:00 UTC),通常T0 = 0;X为时间步长(time step),即口令的有效周期。OATH 标准默认X = 30秒,这也是安当 OTP 采用的默认值。
客户端在时刻t(Unix 时间,单位秒)计算时间计数器:
C = (t - T0) / X例如现在是 2026-08-28 12:00:15 UTC,t = 1787980815,T0 = 0,X = 30,则:
C = 1787980815 / 30 = 59599360(整除,向下取整)注意这里用的是整数除法(向下取整),所以只要t落在C*X到(C+1)*X - 1这一整段 30 秒窗口内,算出来的C都是同一个值。这就是为什么你屏幕上的 6 位码会"每 30 秒变一次,且这 30 秒内保持不变"。
这个C就是 TOTP 的"唯一变量"。双方只要时钟大致同步(误差远小于 30 秒即可,实际系统还会允许前后一个窗口的漂移来容错),就会算出同一个C。
四、HMAC:从"共享秘密 + 时间"生成定长摘要
有了时间计数器C,下一步是用一个带密钥的哈希函数把"密钥 + 时间"搅拌成一段看起来随机、但双方可复现的字节串。这一步用的就是HMAC(Hash-based Message Authentication Code,基于哈希的消息认证码)。
HMAC 的公式(RFC 2104)为:
HMAC(K, m) = H( (K ⊕ opad) || H( (K ⊕ ipad) || m ) )其中:
K是共享密钥;m是消息,在 TOTP 里就是时间计数器C;H是哈希函数,可以是 SHA1 / SHA256 / SHA512 / SHA224 / SHA384,国密场景下则是 SM3;opad、ipad是固定的填充常量。
把m换成我们的时间计数器C(8 字节的大端整数),就得到 TOTP 的核心:
HS = HMAC(K, C)HS(HMAC-SHA1 下是 20 字节,SHA256 是 32 字节,SM3 是 32 字节)是一段高熵的二进制摘要。它本身很长、不可读,我们需要把它"压"成用户能输入的 6 位十进制数字。
五、动态截断:把字节串变成 6 位数字
TOTP(RFC 6238 引用 RFC 4226 的 HOTP 截断法)用一个叫Dynamic Truncation(动态截断)的技巧,从HS里取 4 个字节,再转成十进制并取模。
步骤如下:
- 取
HS的最后一个字节的最低 4 位作为偏移量:
offset = HS[最后字节] & 0x0F- 从
offset处取 4 个字节,拼成一个 31 位整数(最高位清零,防止符号问题):
P = ((HS[offset] & 0x7F) << 24) | ((HS[offset+1] & 0xFF) << 16) | ((HS[offset+2] & 0xFF) << 8) | (HS[offset+3] & 0xFF)- 取模得到指定位数(默认 6 位)的验证码:
TOTP = P mod 10^DigitCount // DigitCount = 6由于P是一个 31 位正整数(范围约 0 ~ 2^31),对10^6 = 1000000取模后,得到一个 0 到 999999 之间的数,不足 6 位时左侧补零,最终呈现为6 位十进制码。
到这里,完整的 TOTP 公式可以写作(RFC 6238 形式):
TOTP = Truncate( HMAC(K, (t - T0) / X) ) mod 10^DigitCount一句话总结:TOTP = HMAC(密钥, 时间计数器) 后动态截断取 6 位。客户端和服务端用同一个K、同一个t、同一个X,必然算出同一个 6 位码——不需要通信,只需要在时间窗口内各自算一遍。
以安当 OTP 为例,手机令牌内部就是按上述标准流程实现:持有与安当 ASP 服务端一致的共享密钥,每 30 秒用 SM3/SHA 系列算法重算一次,屏幕上刷新一个 6 位动态码,并清晰地显示该码的有效时长,让用户心里有数。
六、密钥从哪来:base32 与 hex 编码
读者可能问:客户端和服务端怎么"共享同一个密钥"?不可能把密钥明文发一遍,那不又泄露了吗?
实际流程是:
- 服务端(如安当 ASP)在创建用户时,生成一段随机密钥(通常 160 位 / 20 字节以上),并用密钥与用户身份绑定。
- 服务端把密钥以base32 编码的形式展示成一个二维码(或以 hex 编码展示为一串十六进制串)。
- 用户用安当令牌 APP扫码,APP 把密钥解码后安全存放在本地(与这部手机捆绑,且可用 APP 锁或生物解锁保护)。
- 此后双方就持有同一份
K。密钥只在"扫码注册"这一瞬间从服务端到客户端走了一次,且走的是二维码这种本地光学通道,不经过网络;之后口令生成与验证全程不再传输密钥。
为什么用 base32 而不是 hex?base32 只含大写字母 A–Z 和数字 2–7,不含易混淆的 0/O、1/I/L,方便人工抄写与扫码识别;hex 则更紧凑,适合程序间导出导入。安当 OTP 同时支持 base32 / hex 两种编码,方便企业批量分发与备份。
一个小提醒:密钥就是一切。谁拿到密钥,谁就能算出所有未来的动态码(除非密钥轮换)。所以服务端密钥要加密存储、硬件令牌要防拆,手机令牌要设 APP 锁。这也是为什么安当令牌支持"客户端密码或生物解锁"——即便手机丢了,别人也开不了你的令牌、看不到你的密钥。
七、防重放:时间窗口让截获的口令"过期"
现在来回答 TOTP 最关键的两条安全性质。
重放攻击(Replay Attack):攻击者截获了你刚刚输入的 6 位码,试图在另一台机器或稍后重放这个码来冒充你。
TOTP 怎么防?靠时间窗口 + 已用计数器记录。
- 服务端校验时,不但要算出的码和提交的一致,还要检查这个时间窗口的码"是否已经被用过"。一旦某个窗口
C对应的码成功验证过,服务端会记下来,后续同一个C的重放直接拒绝。 - 即便攻击者赶在下一次刷新前(30 秒内)重放,只要首次验证已经消费了这个窗口,重放立即失效。
- 而且 30 秒后码本身就变了,截获的旧码天然作废。
也就是说,动态码是自带有效期的一次性门票。想重放,难度极高、时间窗极窄,且服务端有显式的"防重放"校验。这也是"一次性密码"名字的由来:用一次就废。
八、防暴力:一次一密让穷举失去意义
暴力破解 / 在线猜测:攻击者对着登录接口狂试 000000、000001……直到蒙对。
6 位十进制只有 100 万种组合,理论上 GPU 也能刷。但 TOTP 让这种攻击基本失效,原因有三:
- 一次一密:正确的码每一刻都在变。攻击者猜 123456,但此刻服务端期待的是 873204;等他试到 873204,时间窗口早过了,正确答案又变成别的数。静态的"猜密码字典"在这里完全没用。
- 服务端限速与锁定:正规实现会对连续失败做限流、封禁、告警。安当 ASP 这类认证系统会在多次失败后进行锁定与审计记录,配合业务系统把爆破挡在门外。
- 验证在服务端:攻击者拿不到密钥,只能对着"网络验证接口"盲猜,每一次猜测都有网络延迟、有失败计数、有风控,成本和被发现的几率极高。
结论:TOTP 把"猜一个长期不变的密码"变成了"在 30 秒内盲猜一个一直在变的数",暴力破解的经济性被彻底打穿。
九、为什么口令从不经网络传输是大事
再强调一次,这是 OTP 区别于"短信验证码"等方案的根本优势之一:
- 短信验证码:口令由服务端生成后通过短信网关下发到手机。这条短信经过运营商网络,存在被 SIM 交换、短信拦截、信令劫持的风险;且口令在传输链路上是明文短信。
- 动态口令(TOTP):口令由客户端本地算出来,密钥从不在网络上跑。网络上只出现一个"此刻的 6 位码",它没有密钥信息、三十秒作废、用过即废。
以安当 OTP 为例,手机令牌生成动态码的过程完全是离线、无通信的:在飞机上、在没有信号的地下室、在完全隔离的内网环境里,只要手机有电、时钟准,令牌照样能出码。这不是 bug,而是设计——因为出码根本不需要联网。口令不以明文秘密的形式在网络上流动,中间人即便嗅探到登录流量,拿到的也只是一次性、已过期、不可复用的数字。
这也带来一个运维上的好处:口令生成不依赖任何外部服务可用性。很多方案把"生成验证码"和"验证验证码"都放在云端,云一抖,全员登录不了;TOTP 把生成下沉到用户设备,验证留在服务端,两侧解耦,韧性更好。
十、时钟同步与漂移:现实世界的容错
理想情况下双方时钟完全一致。现实中,手机时钟可能被用户手调过,服务器也可能有微小偏差。TOTP 用两个手段兜底:
- 容忍前后窗口:服务端校验时除当前窗口
C外,还允许校验C-1、C+1(即前后各 30 秒)的码。这样时钟偏差在 ±30~60 秒内都能正常登录。安当 ASP 可配置允许的窗口漂移数。 - 客户端时钟基准:手机令牌用的是设备系统时钟,现代智能手机都通过 NTP 自动校时,偏差通常在毫秒级,几乎不会触发漂移问题。
需要分清:TOTP 需要的不是"绝对精准的时钟",而是"客户端与服务端时钟大致对齐"。这比很多人想象的宽松得多。
十一、算法选择:SHA1 真的过时了吗
TOTP 标准最初基于 HMAC-SHA1,于是常有人问"SHA1 被攻破,TOTP 还安全吗"。
要点:SHA1 的碰撞攻击针对的是"找到两个不同文件有相同哈希",而 HMAC 的安全性依赖的是原像抗性与密钥保密,并非抗碰撞。迄今没有可行的针对 HMAC-SHA1 的实用攻击,因此 HMAC-SHA1-TOTP 在现实中仍被广泛使用(谷歌验证器、微软验证器默认就是 SHA1/30s/6位)。
但出于合规与前瞻,安当 OTP 支持SHA256 / SHA512 / SHA224 / SHA384乃至国密SM3,企业可在安全策略要求更强_hash、或信创/密评场景要求国密时,直接切换算法而无需改动架构。算法只是 HMAC 里的H,上面的推导全程不变——换个哈希函数,TOTP 照常工作。这一点我们在第 4 篇(国密 SM3 与信创合规)会展开。
十二、一个最小可运行的推导示例(Python 思路)
下面给出一个不依赖任何库、能帮助你"眼见为实"的推导片段(生产环境请用成熟库如pyotp,这里仅为讲清原理):
importhmac,hashlib,struct,timedeftotp(secret:bytes,digits=6,period=30,algo=hashlib.sha1,t=None):t=torint(time.time())counter=(t//period)# 时间计数器 Cmsg=struct.pack(">Q",counter)# C 编码为 8 字节大端hs=hmac.new(secret,msg,algo).digest()# HMAC(K, C)offset=hs[-1]&0x0F# 动态截断偏移p=((hs[offset]&0x7F)<<24|(hs[offset+1]&0xFF)<<16|(hs[offset+2]&0xFF)<<8|(hs[offset+3]&0xFF))returnstr(p%(10**digits)).zfill(digits)# 服务端和客户端用同一 secret(base32 解码得到),各自调用 totp()你会看到:同一份secret、同一时刻调用,两端返回同一个 6 位串;过了 30 秒再调,数字就变了。这 20 行代码,就是整个 TOTP 的灵魂。
十三、常见误区澄清
- 误区 1:动态码是服务端发给我看的。错,是客户端本地算的,服务端只验证。
- 误区 2:我手机没网就登不了。错,出码不需要网;只是登录时把码输给业务系统那一刻需要网。
- 误区 3:6 位太短不安全。单看 6 位确实只有百万组合,但配合"30 秒过期 + 一次一密 + 服务端限速",暴力破解不可行。要更强可改 8 位,但 6 位已是安全与易用性的经典折中。
- 误区 4:用了 OTP 就不需要密码了。OTP 是"第二因素",它证明"你持有这个令牌设备",但最好和密码组合成"所知(密码)+ 所有(令牌)"的双因素,这才是安当 OTP 推荐的双重保险。
十四、落地建议:在你的系统里怎么用
如果你是企业工程师,想把动态口令接进现有系统,记住三件事:
- 别自己造轮子:密钥生成、base32 编码、HMAC 截断、防重放状态机都有成熟标准实现,直接用 OATH TOTP 兼容库或安当 OTP 这类成熟产品,避免密码学实现错误。
- 密钥要当资产管:服务端密钥库必须加密,导出需审批,支持用户丢失令牌后的密钥重置/解绑。
- 做好用户体验:允许前后窗口漂移、提供备用码(scratch codes)、支持扫码注册,能极大降低员工抵触。安当令牌 APP 的扫码注册、有效时长显示、APP 锁/生物解锁,正是为这些体验细节而设计。
十五、FAQ
Q1:手机时间和服务器差很多会怎样?
时差超过允许的漂移窗口(默认前后各一个 30 秒窗口)会登录失败。让用户校时(开启自动校时)即可,手机令牌本身不建议手动改时间。
Q2:密钥泄露了怎么办?
在安当 ASP 后台解绑该用户令牌并重新扫码注册,服务端密钥立即轮换,旧令牌出的码全部失效。
Q3:TOTP 能防钓鱼吗?
能防"凭据复用"类钓鱼——攻击者钓到的一次性码很快过期无法重放。但纯 TOTP 不防"实时中转"型钓鱼代理(攻击者同时把你引到假站并实时转发码)。对钓鱼要求极高的场景,可叠加 FIDO2(设备/生物绑定、域名绑定)。这部分我们在第 2 篇选型对比细说。
Q4:能不能完全离线部署?
可以。安当 OTP 支持本地化部署服务端(安当 ASP),密钥与验证都在企业内网,手机令牌出码本就离线,整体不依赖任何公网服务。
Q5:一个用户能绑多个令牌吗?
可以,常用于"主手机令牌 + 备用硬件令牌"或"手机 + 平板双令牌"的冗余,防止单设备丢失导致锁死。
十六、TOTP 与 HOTP:时间为何胜过计数器
讲 TOTP 绕不开它的前身HOTP(HMAC-Based One-Time Password,RFC 4226)。HOTP 用的不是"时间计数器",而是一个事件计数器:每用一次,计数器加一,验证码随之变化。两者公式几乎一样,差别只在C的来源——HOTP 的C是"用了几次",TOTP 的C是"现在第几个时间窗口"。
HOTP 的问题是"计数器同步":服务端必须知道客户端当前到第几了,一旦某次验证因网络抖动没完成、或用户误触了硬件令牌的按钮,两侧计数器就会错位,需要"向前试探 N 个窗口"来重新对齐,体验磕绊。TOTP 把变量从"事件"换成"时间",而时间是双方都天然拥有、无需额外同步的公开量,于是彻底消灭了计数器错位的麻烦——这是工程上一次漂亮的"用公共变量替代私有状态"的优化。
代价是 TOTP 需要时钟大致同步,但如前所述,±30~60 秒的容错已经足够宽松。综合来看,TOTP 在易用性上全面胜出,这也是它成为当今主流、安当 OTP 默认采用 30 秒时间窗口的根本原因。理解 HOTP 有助于你读懂 TOTP 标准里那句"TOTP is an HOTP where the counter is the time"——它们本就是一家。
十七、TOTP 在等保 2.0 身份鉴别里的定位
很多政企客户关心:上了动态口令,对等保 2.0 有帮助吗?答案是明确的。等保 2.0 三级及以上系统,在"身份鉴别"控制项里通常要求"应采用两种或两种以上组合的鉴别技术对用户身份进行鉴别,且其中一种应为密码技术"。TOTP 这类基于 HMAC(密码技术)的动态口令,恰好能作为"第二种鉴别因素"满足该条款;若采用 SM3 算法,更能契合"采用合规密码技术"的措辞。安当 OTP 支持国密 SM3,正是为信创与密评场景准备的"合规加分项"。这一点我们会在第 4 篇专门展开,这里先建立"TOTP 既是安全技术、也是合规工具"的认知。
十八、小结
TOTP 的优雅在于用极简的构件(共享密钥 + 时间计数器 + HMAC + 动态截断)解决了"如何让密码每次都不同"这个难题。30 秒窗口挡重放,一次一密挡暴力,密钥不出网挡嗅探。它不是银弹,却是性价比最高、部署最轻、兼容性最广的双因素方案之一——谷歌、微软、腾讯验证器,以及安当 OTP,底层都是同一套标准。
理解了原理,你会更放心地把它用在业务系统登录、堡垒机、云桌面接入、GitLab 等远程接入的二次认证上。下一篇,我们把 OTP 放到"双因素技术全家福"里,和硬件 UKey、FIDO2 做一次硬碰硬的选型对比。
方案参考
安当 OTP是上海安当技术推出的动态口令身份认证产品,基于 OATH TOTP 国际标准构建,核心能力如下:
- 标准与参数:口令周期 30 秒、长度 6 位,算法支持 SHA1 / SHA256 / SHA512 / SHA224 / SHA384 / 国密 SM3;密钥采用 base32 / hex 编码。
- 双端组成:客户端为安当令牌 APP(Android / iOS,扫码注册、随时间长出 6 位动态码、密钥与手机捆绑、出码过程离线无通信、支持 APP 锁或生物解锁、显示有效时长),与服务端 andang-ASP 身份认证服务系统组合提供双因子方案;同时提供硬件令牌。
- 对接能力:通过 Radius 协议与 API 两种方式对接业务系统;支持本地化部署或安当公有云 SaaS;提供用户管理与日志审计、用户自注册、一个后台对接多应用/分应用管理;管理员支持 FIDO。
- 合规与成本:支持国密 SM3,满足信创与等保、密评要求;相比硬件 Key 成本更低,SaaS 按天弹性计费、零运维、可当天上线;手机令牌完全兼容谷歌 / 微软 / 腾讯验证器。
如需进一步了解产品能力,可前往安当官网查询 OTP 产品详情。