惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
雷峰网
雷峰网
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
博客园 - Franky
腾讯CDC
T
Tailwind CSS Blog
Recent Announcements
Recent Announcements
V
V2EX
N
Netflix TechBlog - Medium
量子位
Jina AI
Jina AI
Y
Y Combinator Blog
The GitHub Blog
The GitHub Blog
G
Google Developers Blog
爱范儿
爱范儿
博客园 - 叶小钗
D
Docker
MongoDB | Blog
MongoDB | Blog
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss

Donghai's Blog

使用DBeaver连接Dynamics 365 Dataverse | Donghai C# 技术备忘 - LINQ | Donghai 为Astro站点添加Google Analytics | Donghai C# 技术备忘 - LINQ - Donghai’s Blog 20260819 说些什么 | Donghai Dynamics 365 interview questions | Donghai C# 技术备忘 (1) | Donghai Dynamics 365 事件框架、事件执行管道 | Donghai 马拉松配速表 | Donghai 马拉松赛事分级 | Donghai 抖音创作日志(2026年) | Donghai 2026年跑步日志(5月开始) | Donghai 初尝Github Actions | Donghai AstroPaper主题添加Waline评论 | Donghai Dynamics 365集中视图/聚焦视图/Focused View | Donghai AstroPaper主题添加GitHub风格的Markdown警告框 | Donghai AstroPaper主题自定义记录 | Donghai 我日常收藏的优质网站资源导航(持续更新) | Donghai 如何从归档页批量获取URL并提交到Bing Webmaster Tools | Donghai PaperMod 使用 Zeoseven 自定义网页字体 | Donghai 我常用的Prompt | Donghai Hugo默认时区导致本地预览文章不显示 | Donghai 通过手动提交工单解决Bing不收录网站问题 | Donghai 如何访问 Power BI 管理门户(Power BI Admin Portal) | Donghai 还在手敲时间戳?Win11输入法一个技巧,秒输当前时间戳 | Donghai 工作术语备忘录(持续更新) | Donghai 使用XrmToolBox导出安全角色表级权限到Excel | Donghai 24年的年终总结 | Donghai World笔记 | Donghai Excel笔记 | Donghai
2FA | Donghai
Donghai · 2026-04-28 · via Donghai's Blog

越来越多项目要求启用2FA,不得不去了解它!

Table of contents

Open Table of contents
  • 概念
  • 2FA 方案
    • 方案(一)密码 + 硬件令牌
    • 方案(二)密码 + 手机短信验证码
    • 方案(三)密码 + 基于时间的一次性密码(TOTP)(当前主流且推荐的方案)
  • TOTP的算法
  • TOTP的实现(Csharp)
  • 参考

概念

  1. 2FA 即:双因素认证 <=> Two-factor authentication <=> 2FA
  2. 双因素认证就是指,通过认证同时需要两个因素的证据,例如银行卡就是最常见的双因素认证,我们必须同时提供“银行卡”、“密码”两种信息才能拿钱

2FA的核心是组合2种不同类型的验证因子,验证因子主要分为以下3类:

  • 你知道的东西:密码、PIN码等
  • 你拥有的东西:手机、电脑安全密钥等,通过获取一次性验证码(OTP)或确认推送通知来完成验证
  • 你是什么:指纹、虹膜等

2FA 方案

在实际应用中,最常用的 2FA 方案是 “密码 + 你拥有的东西”,因为生物特征(第三因素)需要特殊硬件支持,普适性稍差

方案(一)密码 + 硬件令牌

例如银行的U盾、以前网络游戏(如DNF)的密保卡、登录GitHub或系统用的YubiKey。用户插入设备或输入设备上动态码后,再输入密码

方案(二)密码 + 手机短信验证码

国内最常见的方案。登录时向绑定手机发送一条包含短时验证码的短信。注意:此方案安全性相对较低,因为短信可能被拦截、SIM卡可能被克隆,但胜在便捷

方案(三)密码 + 基于时间的一次性密码(TOTP)(当前主流且推荐的方案)

这是公认较可靠的软件层面2FA方案,国际标准为RFC6238 。用户需要先在自己的手机上安装一个身份验证器App(如 Google Authenticator、Microsoft Authenticator、Authy 等)

  • 第一步(绑定):服务端生成一个密钥,以二维码形式展示给用户。
  • 第二步(保存):用户用App扫描二维码,密钥安全保存到手机(也就说这个密钥在手机和服务端上都存有了)。注意,密钥必须跟手机绑定。一旦用户更换手机,就必须生成全新的密钥
  • 第三步(验证):登录时,App使用密钥和当前时间戳,通过特定算法(如HMAC-SHA1)生成一个30秒或60秒(默认30秒)内有效的6-8位动态验证码。用户在有效期内,把这个动态验证码提交给服务器。服务器也使用密钥和当前时间戳,生成一个动态验证码,跟用户提交的验证码比对。只要两者不一致,就拒绝登录

TOTP的算法

TOTP 的全称是”基于时间的一次性密码”(Time-based One-time Password),仔细看上面的步骤,我会有一个问题:App和服务端,如何保证30秒期间都得到同一个验证码?答案就是下面的这个公式:

TC = floor((unixtime(now)  unixtime(T0)) / TS)
  • TC —> 时间计数器
  • unixtime(now) —> 当前 Unix 时间戳
  • unixtime(T0) —> 约定的起始时间点的时间戳,默认是0,也就是1970年1月1日
  • TS —> 哈希有效期的时间长度,默认是30秒

因此,上面的公式就变成下面的形式

TC = floor(unixtime(now) / 30)

所以,只要在 30 秒以内,TC 的值都是一样的。前提是服务端和手机端的时间必须同步。接下来,就可以算出验证码了。

TOTP = HASH(SecretKey, TC)

上面代码中,HASH就是约定的哈希函数,默认是 SHA-1

另外,TOTP 有“硬件生成器”和“软件生成器”之分,都是采用上面的算法

(左:硬件生成器,右:软件生成器) TOTP硬件和软件生成器

TOTP的实现(Csharp)

(1)NuGet 引入 SimpleBase 包

(2)简单实现

using System;
using System.Security.Cryptography;
using SimpleBase;

public class SimpleTotp
{
    public static string Generate(string base32Secret)
    {
        // 1. 解码 Base32 密钥
        byte[] secret = Base32.Rfc4648.Decode(base32Secret);

        // 2. 计算时间计数器 (30秒一个窗口)
        long counter = DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 30;
        byte[] counterBytes = BitConverter.GetBytes(counter);
        if (BitConverter.IsLittleEndian) Array.Reverse(counterBytes);

        // 3. HMAC-SHA1
        byte[] hash;
        using (var hmac = new HMACSHA1(secret))
        {
            hash = hmac.ComputeHash(counterBytes);
        }

        // 4. 动态截取
        int offset = hash[^1] & 0x0F;
        int code = ((hash[offset] & 0x7F) << 24) |
                   ((hash[offset + 1] & 0xFF) << 16) |
                   ((hash[offset + 2] & 0xFF) << 8) |
                   (hash[offset + 3] & 0xFF);

        code %= 1000000;
        return code.ToString("D6");
    }
}

参考

  1. 双因素认证(2FA)教程

(完)

如果这篇文章刚好帮到了你,欢迎请我喝杯咖啡