PythonTOTP双因素认证
Python TOTP 双因素认证实战双因素认证2FA为账户增加一层安全保障。TOTP基于时间的一次性密码是最流行的 2FA 方案Google Authenticator 和 Authy 都支持此标准。1. 安装依赖------------# pip install pyotp qrcode pillowimport pyotpimport qrcodeimport qrcode.image.svgimport ioimport base64import timefrom typing import List, Optional2. TOTP 基础生成密钥与验证-----------------------------def generate_totp_secret() - str:生成 TOTP 共享密钥Base32 编码# pyotp.random_base32() 生成一个随机的 Base32 密钥# 长度默认 32 字符160 位熵secret pyotp.random_base32()return secretdef create_totp(secret: str) - pyotp.TOTP:使用密钥创建 TOTP 对象# TOTP 默认使用 SHA1、30 秒时间窗口、6 位数字return pyotp.TOTP(secret)# 演示生成密钥并获取当前一次性密码secret generate_totp_secret()print(fTOTP 密钥: {secret})totp create_totp(secret)current_code totp.now() # 获取当前时间窗口的验证码print(f当前验证码: {current_code})# 等待几秒后验证模拟用户输入time.sleep(1)is_valid totp.verify(current_code)print(f验证码有效: {is_valid})# 验证过期的验证码expired_code totp.at(for_timeint(time.time()) - 120) # 2 分钟前print(f过期验证码: {expired_code}, 有效: {totp.verify(expired_code)})3. TOTP 参数定制-----------------def create_custom_totp(secret: str) - pyotp.TOTP:创建自定义参数的 TOTP 实例return pyotp.TOTP(secret,digits8, # 验证码位数默认 6interval60, # 时间窗口秒默认 30digestpyotp.SHA256 # 哈希算法默认 SHA1)custom_totp create_custom_totp(secret)print(f自定义 TOTP: {custom_totp.now()} (8 位, 60 秒))4. HOTP基于计数器的哈希---------------------------# HOTP 使用计数器而非时间每次验证成功后计数器递增def create_hotp(secret: str, initial_count: int 0) - pyotp.HOTP:创建 HOTP 实例return pyotp.HOTP(secret, initial_countinitial_count)hotp create_hotp(secret)# 生成第 1、2、3 个验证码for i in range(3):code hotp.at(i)print(fHOTP 第 {i1} 次: {code})# 验证时会自动使用当前计数器print(f 验证: {hotp.verify(code, counteri)})# HOTP 适用于离线场景或无法同步时间的设备# 但需要维护计数器同步比 TOTP 更复杂5. 生成二维码用于用户绑定------------------------------def generate_qr_code_uri(secret: str, username: str,issuer: str MyApp) - str:生成 otpauth URI 供二维码使用# 标准格式: otpauth://totp/ISSUER:USERNAME?secretSECRETissuerISSUERtotp pyotp.TOTP(secret)uri totp.provisioning_uri(nameusername, issuer_nameissuer)return uridef create_qr_code_image(uri: str) - str:生成二维码并返回 Base64 编码的 SVGqr qrcode.make(uri, image_factoryqrcode.image.svg.SvgImage)buf io.BytesIO()qr.save(buf)svg_data buf.getvalue().decode()# 转为 HTML 可内联的 Base64实际使用b64 base64.b64encode(buf.getvalue()).decode()return fdata:image/svgxml;base64,{b64}# 生成用户的绑定 URIuri generate_qr_code_uri(secret, userexample.com, MySecureApp)print(fProvisioning URI:)print(f {uri})# 二维码生成实际部署时显示给用户扫描# 用户使用 Google Authenticator / Authy 扫码即可完成绑定6. 验证流程服务端核心逻辑-----------------------------class TOTPAuthenticator:TOTP 双因素认证管理器def __init__(self, secret: str):self.secret secretself.totp pyotp.TOTP(secret)# 允许前后各 1 个时间窗口的偏移共 3 个窗口self.allowed_drift 1def verify_code(self, code: str) - bool:验证用户输入的验证码允许一定的时间偏移。valid_window 参数允许前后各 N 个时间窗口的偏移return self.totp.verify(code, valid_windowself.allowed_drift)def get_current_code(self) - str:获取当前验证码仅用于调试或测试return self.totp.now()# 模拟完整的验证流程auth TOTPAuthenticator(secret)user_input auth.get_current_code()print(f\n用户输入验证码: {user_input})print(f服务端验证结果: {auth.verify_code(user_input)})7. 备份码Recovery Codes---------------------------def generate_backup_codes(count: int 8) - List[str]:生成一次性备份恢复码import secretscodes []for _ in range(count):# 生成 10 位十六进制编码code secrets.token_hex(5).upper()# 格式化为分组形式便于输入formatted f{code[:5]}-{code[5:]}codes.append(formatted)return codesdef store_backup_codes(codes: List[str]) - List[dict]:返回带哈希的备份码用于存储import hashlibstored []for code in codes:# 存储哈希值而非明文一次验证后立即销毁code_hash hashlib.sha256(code.encode()).hexdigest()stored.append({hash: code_hash, used: False})return stored# 生成 8 个备份码backup_codes generate_backup_codes(8)print(\n备份恢复码请立即保存到安全位置:)for i, code in enumerate(backup_codes, 1):print(f {i}. {code})# 备份码应显示一次后就不可再查看# 每个备份码只能使用一次用过后立即作废8. 时间偏移处理----------------def handle_time_drift(secret: str, user_code: str) - dict:处理客户端与服务器之间的时间偏差。某些手机时间可能不准确导致验证失败。totp pyotp.TOTP(secret)# pyotp 的 verify 已内置 valid_window 参数# 但可以手动检测偏量用于日志expected_time int(time.time())matched Falsefor offset in range(-3, 4):check_time expected_time (offset * 30)if totp.at(for_timecheck_time) user_code:matched Trueprint(f [调试] 在偏移 {offset} 个窗口处匹配)breakreturn {verified: matched,server_time: expected_time,drift_detected: matched}# 任何成功验证都可以记录当前时间偏移用于后续优化drift_result handle_time_drift(secret, auth.get_current_code())print(f\n时间偏移检测: {drift_result})9. 生产环境集成注意事项------------------------def totp_setup_for_new_user(username: str) - dict:为新用户初始化 TOTP 的完整流程# 1. 生成密钥secret generate_totp_secret()# 2. 生成二维码 URIuri generate_qr_code_uri(secret, username)# 3. 生成备份码recovery_codes generate_backup_codes(8)# 4. 返回设置信息实际项目中需要绑定到用户return {secret: secret, # 存入用户表的 totp_secret 字段qr_uri: uri, # 显示给用户扫码recovery_codes: recovery_codes, # 显示给用户保存enabled: False # 待用户验证后才启用}def verify_and_enable_totp(secret: str, user_code: str) - bool:用户首次设置时验证一次以确认绑定成功totp pyotp.TOTP(secret)return totp.verify(user_code)# 示例流程new_user totp_setup_for_new_user(aliceexample.com)print(f\n新用户 TOTP 设置完成密钥: {new_user[secret]})10. 安全建议------------# 1. 密钥传输必须使用 HTTPS# 2. 二维码只在首次设置时显示一次# 3. 备份码显示后应立即存储到安全位置# 4. 设置速率限制防止暴力枚举 6 位验证码百万分之一概率# 5. 监控异常的大量验证失败尝试# 6. 记录 TOTP 验证成功的设备信息和 IP# 7. 支持用户撤销所有已绑定的 TOTP 设备11. 总结---------TOTP 为应用提供了强大的第二层认证。pyotp 库实现简洁与主流验证器应用完全兼容。结合备份码和合理的时间偏移容错可以构建安全可靠的 2FA 系统显著提升账户安全性。