像 Google Authenticator 或 Microsoft Authenticator 这样的身份验证器,在手机 没有网络、甚至开着飞行模式 的情况下,依然能不断生成新的验证码。这些验证码还可以成功登录网站。
那问题就来了:
手机既没有联网,也没有和服务器通信,它是怎么知道正确验证码的?
答案是:验证码并不是服务器发给手机的,而是 手机和服务器各自算出来的。
一、验证码其实是算出来的
Authenticator 使用的是一种标准算法,叫做 TOTP(Time-based One-Time Password,基于时间的一次性密码)。
它的核心思路是:手机和服务器提前共享一把"秘密钥匙",然后按照同样的算法,根据当前时间计算出验证码。
简单来说:
验证码 = 算法(密钥 + 当前时间)
只要三样东西一致:
-
密钥
-
算法
-
当前时间
手机和服务器就会得到 完全一样的验证码。
所以整个过程是这样的:
| 手机 | 服务器 |
|---|---|
| 保存密钥 | 保存同一把密钥 |
| 使用同一算法 | 使用同一算法 |
| 使用当前时间 | 使用当前时间 |
结果就是:
双方各自计算,但得到的是同一个6位验证码。
这就是为什么 Authenticator 完全离线也能使用。
二、二维码其实是一把钥匙
当网站要求你绑定 Authenticator 时,通常会展示一个二维码。
这个二维码里面会包含一段信息,例如:
secret = JBSWY3DPEHPK3PXPalgorithm = SHA1period = 30digits = 6
其中最重要的是:
secret
也就是 共享密钥。
绑定时,服务器和手机会同时保存这把密钥。
从这一刻开始,双方就拥有了同一把钥匙。
以后只要用这把钥匙,加上当前时间,就可以算出验证码。
三、验证码为什么每 30 秒变化
TOTP 并不是直接使用当前时间,而是把时间 切成一段一段的时间片。
最常见的是 30 秒一个时间片。
例如:
| 时间 | 验证码 |
|---|---|
| 12:00:00 -- 12:00:29 | 483921 |
| 12:00:30 -- 12:00:59 | 117502 |
| 12:01:00 -- 12:01:29 | 639244 |
每过 30 秒:
时间片变化 → 算法输入变化 → 验证码变化。
所以验证码会不断刷新。
四、如果修改手机时间会发生什么
既然验证码是根据 当前时间 计算的,那如果用户手动修改手机时间,会不会影响使用?
答案是:会。
因为算法依赖时间。
如果手机时间被调快或调慢,例如:
真实时间:12:00手机时间:12:10
那么手机算出来的验证码就会和服务器不一致。
结果就是:验证码会验证失败。
不过很多服务器会做一定的容错处理。
例如允许:
当前时间片前一个时间片后一个时间片
也就是说,如果验证码落在 ±30秒甚至±60秒 的范围内,服务器仍然会接受。
这样就能容忍一点时间误差。
但如果手机时间偏差太大,比如几分钟甚至几小时,那验证码就一定会失效。
五、服务器是如何解决时区问题的
很多人可能会产生新的好奇:
用户在中国,美国,欧洲,时区不同,那验证码会不会不一样?
实际上并不会,因为 TOTP 使用的并不是本地时间,而是 Unix 时间戳。
Unix 时间戳的定义是:从 1970 年 1 月 1 日 00:00:00 UTC 开始经过的秒数。
例如:
1710000000
这个数字在全世界都是一样的。
无论你在:
-
北京
-
东京
-
纽约
-
伦敦
同一时刻的 Unix 时间戳是完全一致的。
所以算法其实使用的是:
timeStep = Unix时间 / 30
而不是:
当前时区时间
因此时区完全不会影响验证码。
唯一重要的只有一件事:手机时间是否准确。
六、为什么验证码只有 6 位也很安全
很多人会觉得 6 位数字只有
1000000 种组合
好像很容易被猜中。
但实际上并不容易,因为:
1️⃣ 网站通常会限制尝试次数
例如 3~5 次失败就锁定。
2️⃣ 验证码每 30 秒就会变化
攻击者几乎没有时间暴力尝试。
所以在现实环境中,这种机制是非常安全的。
七、一句话总结
Authenticator 的工作原理其实非常优雅:手机和服务器提前共享一把密钥,然后根据当前时间用同一算法计算验证码。
因此它可以:
-
完全离线运行
-
不需要短信
-
不需要网络
而服务器只需要验证计算结果是否一致。
换句话说,Authenticator 不是在接收验证码,而是在 自己生成验证码。
这也是为什么,即使手机没网,它依然可以正常工作。