双因素身份验证
· 阅读需 8 分钟
一、基础定义
双因素认证2FA(Two-Factor Authentication):登录 / 操作账号时,必须同时提供两种不同类型的身份凭证,才能完成身份校验,是多因素认证(MFA)最常用的子集。传统登录仅单因素:账号 + 密码(单一知识凭证),一旦密码泄露,账号直接被盗;2FA 在密码之外增加第二道独立校验屏障,黑客只拿到密码也无法登录。
核心逻辑:三大身份凭证分类(行业标准)
所有验证方式只会归为以下三类,2FA 要求必须选自两类不同组别,不能同组叠加:
- 你知道的东西(知识类)密码、密保问题、PIN 码、生日等。风险:容易泄露、被钓鱼、暴力破解。
- 你拥有的东西(持有类)手机、硬件密钥、验证码 App、U 盾、短信、邮箱、专用令牌。GitHub TOTP 验证码、手机短信、YubiKey 都属于这一类。
- 你本身的特征(生物类)指纹、人脸、虹膜、声纹。
举个例子:
- 密码(知道)+ 手机验证码(持有)= 标准 2FA
- 密码(知道)+ 指纹(本身)= 标准 2FA
- 密码 + 密保问题(同属「知道」)≠ 2FA,只是双重密码,无安全提升
二、2FA 主流实现方式(GitHub 全部支持)
1. TOTP 基于时间的一次性密码(你当前使用的方式,GitHub 推荐首选)
标准协议:RFC6238,也就是你之前研究的 6 位 30 秒刷新验证码。
- 原理:服务器与你的设备共享一串 Base32 密钥,依靠统一时间戳,每 30 秒同步生成一组 6 位数字。
- 载体:Microsoft Authenticator、Google Authenticator、Authy、密码管理器,甚至你自己写的 Python 程序。
- 优势:离线可用,不需要手机信号 / 网络;无运营商短信拦截、盗号风险;无额外成本。
- GitHub 场景:截图里扫码绑定二维码、setup key 手动配置,全部是 TOTP。
2. SMS 短信验证码(备用,不推荐主力)
登录时向绑定手机号发送带 6 位数字的国际短信。
- 缺点:SIM 卡劫持、运营商短信拦截、境外短信收不到、短信明文传输易被窃取。
- GitHub 仅作为辅助备份,不建议单独依赖。
3. 邮箱验证码
向绑定邮箱发送一次性登录码,安全性很低,邮箱一旦被盗,2FA 直接失效。
4. 推送 App 验证(GitHub Mobile)
手机安装 GitHub 官方 App,登录时手机弹出确认弹窗,一键批准登录,无需手动输入数字,底层依然是 TOTP。
5. FIDO 硬件密钥(最高安全等级,企业用户)
YubiKey、苹果通行密钥 Passkey,物理硬件设备,通过 USB/NFC/ 蓝牙校验,无法远程劫持,防钓鱼攻击。GitHub 支持绑定硬件密钥作为第二验证因素。
6. 一 次性恢复码(应急兜底)
开启 2FA 时生成 10 组固定一次性代码,仅在手机丢失、无法获取验证码时登录,每组只能使用一次,不属于常规 2FA 方式。
三、2FA 的安全价值(为什么 GitHub 强制要求贡献者开启)
- 抵御密码泄露网站数据库泄露、钓鱼网站窃取密码、弱密码暴力破解场景下,攻击者仅有密码,缺少第二凭证无法登录。
- 防御中间人劫持短信容易被劫持,但 TOTP 硬件密钥无传输过程,黑客无法拦截验证码。
- 防钓鱼攻击FIDO/Passkey 可识别真实域名,仿冒 GitHub 钓鱼页面无法完成验证;TOTP 也大幅提高钓鱼门槛。
- 平台风控强制规则GitHub 政策:只要账号存在代码提交、仓库创建、开源贡献行为,强制开启 2FA,不开启会限制 Git 推送、仓库管理、账号设置等核心功能,对应你之前反复弹出的强制校验弹窗。
四、2FA 与 MFA(多因素认证)的区别
- 2FA:严格2 种不同类型凭证(双因素)
- MFA:≥2 种,可 3 种及以上(密码 + 验证码 + 指纹,三因素)日常语境中大家常混用,但 2FA 特指双因素场景。
五、2FA生成规则
GitHub 验证器 App 使用TOTP(基于时间的一次性密码)国际标准 RFC6238,固定参数如下表格
| 参数 | 固定值 | 说明 |
|---|---|---|
| 加密算法 | HMAC-SHA1 | 哈希计算底层算法 |
| 验证码位数 | 6 位十进制数字 | 最终输出 000000~999999 |
| 时间周期 | 30 秒 | 每 30 秒生成一组新验证码 |
| 密钥编码 | Base32 | 绑定二维码里的密钥是 Base32 字符串,无大小写区分 |
| 时间计数器 | Unix时间戳 // 30 | 用当前秒数整除 30 得到步进值 |
| 容错窗口 | ±1 个周期 | GitHub 服务器会校验上一个 / 当前 / 下一个 30 秒窗口的验证码,解决设备时间微小偏差 |
生成流程
- 获取当前 Unix 时间戳(秒),除以 30 向下取整,得到计数器
T - 将
T转为8 字节大端序二进制数据 - 把 GitHub 给你的 Base32 密钥解码为原始二进制密钥
- 执行
HMAC-SHA1(密钥, 8字节计数器),得到 20 字节哈希结果 - 取哈希最后 1 字节的低 4 位作为偏移量
offset - 从哈希的
offset位置截取 4 字节,清除最高符号位 - 对
10^6取模,不足 6 位前面补 0,得到最终 6 位验证码
生成脚本
生在验证码之前,需要拿到 GitHub 绑定 2FA 时的Base32 密钥,任意编程语言(Python/JS/Go/Java)都能实现生成器。通过扫描你github中的Authenticator app二维码,可以得到一个字符串,otpauth://totp/GitHub:用户名?secret=xxx,secret= 后面就是 Base32 密钥
def generate_totp_code(base32_secret: str, period: int = 30, digits: int = 6) -> str:
"""根据 Base32 密钥生成 TOTP 验证码(RFC 6238)"""
# 1. 当前时间戳除以周期,向下取整得到计数器 T
t = int(time.time())
# 2. T 转为 8 字节大端序二进制
counter_bytes = struct.pack(">Q", t)
# 3. Base32 密钥解码为原始二进制密钥(大写并补齐 padding)
upper = base32_secret.upper()
padded = upper + "=" * ((8 - len(upper) % 8) % 8)
key = base64.b32decode(padded)
# 4. HMAC-SHA1(密钥, 计数器),得到 20 字节哈希
h = hmac.new(key, counter_bytes, hashlib.sha1).digest()
# 5. 取最后一字节低 4 位作为偏移量 offset
offset = h[-1] & 0x0F
# 6. 从 offset 截取 4 字节,清除最高符号位
code_int = struct.unpack(">I", h[offset:offset + 4])[0] & 0x7FFFFFFF
# 7. 对 10^digits 取模,前补零
return str(code_int % (10 ** digits)).zfill(digits)
六、工作完整流程(以 GitHub TOTP 举例)
- 预绑定阶段(你截图的页面)GitHub 服务器生成唯一 Base32 密钥,同时下发给你(二维码 /setup key),服务器永久保存该密钥。
- 登录流程① 用户输入用户名 + 密码(第一因素);② 服务器校验密码正确后,触发第二道 2FA 校验;③ 用户打开验证器 App,基于共享密钥 + 当前时间生成 6 位 TOTP 验证码;④ 提交验证码,服务器用同一密钥、同一时间窗口计算验证码比对;⑤ 校验一致,允许登录;不一致则拒绝访问。
- 容错机制:服务器会校验「上一个 30 秒 / 当前 / 下一个 30 秒」三组验证码,解决手机和电脑微小时间差。
七、常见误区澄清
- 误区:输两次密码就是 2FA错误,两者都属于「你知道的信息」,只是双重密码,没有安全提升。
- 误区:短信是最安全的 2FA错误,短信是风险最高的方案,优先 TOTP 验证器 App。
- 误区:2FA 开启后绝对不会被盗号2FA 大幅提升门槛,但如果泄露 Base32 密钥、丢失硬件密钥、泄露恢复码,账号依然有风险,密钥需要妥善保管。
- 误区:可以绕过 2FA 校验弹窗GitHub 强制校验是后台风控策略,无法跳过,必须完成一次有效 TOTP 验证才能永久消除弹窗。
八、优缺点总结
优点
- 极大降低账号被盗概率;
- TOTP 离线可用,不依赖网络 / 运营商;
- 免费、易部署,个人 / 企业均可使用;
- 主流平台(GitHub、云服务器、支付、后台管理)全部标配。
缺点
- 增加登录操作步骤,降低使用便捷度;
- 若丢失验证设备且未保存恢复码,有账号锁死风险;
- TOTP 对设备系统时间同步有要求,时间偏差会导致验证码失效。