1. 引言
还记得 2023 年 GitHub 强制推行多因子认证(MFA)的那一刻吗?从 3 月开始,GitHub 分阶段要求用户启用 MFA,并在年底前全面完成覆盖,这让全球开发者不得不重新审视身份安全的重要性。
现在我们登录 Github ,除了要输入密码,还需要完成一个额外的验证步骤,比如输入手机上的动态验证码,或者通过手机上的身份验证器(Authenticator App)确认登录。这种看似繁琐的体验已经成为各大云厂商产品的标配。不仅是 GitHub,像 AWS、阿里云、腾讯云等云厂商也几乎都要求在敏感操作时使用多因子认证(MFA),以确保账户安全。
这种举措不仅保护了平台上的代码和账户安全,更体现了现代身份管理技术的趋势,今天,我们就从 GitHub 强制 MFA 的案例切入,了解 MFA 及 Google Authenticator 的实现原理。
2. 什么是 MFA/2FA
在探讨 MFA 之前,我们需要理解身份验证的本质。身份验证是确认某人或某物的身份是否属实的过程。无论是通过密码登录 Gmail,还是刷身份证进入火车站,身份验证的核心都是确保「你是你自称的那个人」。
然而,传统的基于密码的身份验证模式存在诸多隐患:
-
密码过于简单:许多人使用诸如“123456”或“password”这样的弱密码。 -
密码重复使用:用户往往将同一个密码应用于多个网站,一旦一个账户泄露,其它账户也岌岌可危。 -
钓鱼攻击和暴力破解:黑客通过欺骗或技术手段轻易获取用户密码。 -
中间人攻击:在不安全的网络环境中,密码可能被拦截。
这些问题导致密码的安全性备受质疑,因此需要额外的保护层,MFA 由此应运而生。
2.1 MFA:不是多一个步骤,而是多一层防护
MFA,Multi-Factor Authentication,多因子认证,是一种身份验证方法,要求用户提供多个独立的身份验证因素来完成登录或访问。传统的身份认证只依赖单一密码,MFA 则通过引入额外的验证步骤,极大地提升了账户安全性。
在 MFA 中,通常会结合以下三类验证因素:
-
你知道的东西:密码、PIN 码、答案问题等。 -
你拥有的东西:动态验证码(通过手机或硬件设备生成)、安全令牌、智能卡、U 盾等。 -
你自身的特征:生物特征验证,如指纹、面部识别、虹膜扫描等。
MFA 的意义在于,即便攻击者获得了你的密码,由于缺少额外的验证因素,他们依然无法轻易访问你的账户。例如,登录 GitHub 时,即使密码被泄露,攻击者若没有你的手机或安全密钥,仍然无法完成登录。
毕竟,密码泄露已经成为网络攻击中最常见的手段,而 MFA 则为用户的账户增加了第二道甚至第三道锁。
2.2 2FA
2FA 是MFA 的一种特殊形式,它仅使用两种不同的验证因素来完成认证。简单来说,2FA 是 MFA 的一个子集。
例如:
-
登录时输入密码(第一个验证因素:你知道的东西)。 -
然后输入手机上的动态验证码(第二个验证因素:你拥有的东西)。
值得注意的是,两种不同的验证因素是类别的不同,像以前有一种策略是需要提供密码和安全问题答案,这是单因素身份验证,因为这两者都与「你知道的东西」这个因素有关。
在大多数应用中,2FA 已经足够满足安全需求,因此它是目前最常见的多因子认证实现方式。
3. 为什么 MFA 如此重要?
1. 密码不再安全
随着技术的进步,密码破解的门槛越来越低。攻击者可以通过以下方式轻松破解密码:
-
暴力破解:通过快速尝试各种可能的密码组合。 -
数据泄露:黑客通过暗网购买被泄露的用户名和密码。 -
钓鱼攻击:通过伪装成合法网站诱骗用户输入密码。
在这种背景下,仅靠密码保护账户变得极为不可靠。MFA 通过引入多层保护,从根本上提升了安全性。
2. 提高攻击成本
MFA 的最大优势在于,它大幅提高了攻击者的攻击成本。例如,攻击者即便成功窃取了用户密码,也需要物理接触用户的手机或破解生物特征才能完成登录。这种额外的复杂性往往会使攻击者放弃目标。
3. 应对多样化的威胁
MFA 可以有效抵御多种网络威胁,包括:
-
凭证填充攻击:即使用泄露的密码尝试登录多个账户。 -
中间人攻击:即便密码在传输中被窃取,攻击者仍需第二个验证因素。 -
恶意软件:即使恶意软件记录了用户输入的密码,也无法破解动态验证码。
4. MFA/2FA 的工作过程和形式
4.1 MFA 验证的形式
MFA 形式多样,主要有如下的一些形式:
-
基于短信的验证:用户在输入密码后,会收到一条包含验证码的短信。虽然方便,但短信验证并非绝对安全,因为短信可能被拦截或通过 SIM 卡交换攻击(SIM Swapping)被窃取。 -
基于 TOTP(时间同步一次性密码)的验证:像 Google Authenticator 这样的应用程序可以生成基于时间的动态密码。这种方式更安全,因为动态密码仅在短时间内有效,且无需网络传输。 -
硬件令牌:硬件令牌是专门生成动态密码的物理设备。例如银行常用的 USB 令牌,用户需要插入电脑才能完成验证。 -
生物特征验证:指纹、面部识别和视网膜扫描是最常见的生物特征验证方式。这种验证方式非常直观,但存在用户数据隐私的争议。 -
基于位置的验证:通过 GPS 或 IP 地址限制用户只能在特定位置登录。 -
基于行为的验证:通过分析用户的打字节奏、鼠标移动轨迹等行为特征来确认身份。
4.2 2FA 如何工作?
双因素身份验证的核心理念是:即使攻击者获得了用户的密码,他仍然需要通过第二道验证关卡才能访问账户。以下是 2FA 的典型工作流程:
-
第一道验证:用户输入用户名和密码:用户通过密码证明「知道的内容」,这是第一道验证因素。 -
第二道验证:动态代码或生物特征识别:系统会向用户发送一个一次性验证码(如短信、电子邮件或 Google Authenticator 生成的代码),或者要求用户提供指纹或面部识别。这是「拥有的东西」或「自身的特征」的验证。 -
验证成功,授予访问:如果两道验证都通过,用户即可成功登录。
如当你登录阿里云时,输入密码后需要打开阿里云的 APP,输入 MFA 的验证码。
5. MFA 的局限性
尽管 MFA 极大地提高了账户安全性,但它并非万能。有如下的一些局限性:
-
用户体验问题:对于技术不熟练的用户来说,设置和使用 MFA 应用程序门槛比较高。此外,每次登录需要额外的验证步骤,也可能降低用户体验。
-
成本问题:企业需要支付额外的费用来实施 MFA。例如短信验证需要支付短信发送费用,而硬件令牌的采购和分发也需要额外开支。
-
并非百分百安全:MFA 虽然有效,但并非无懈可击。例如:
-
短信验证可能被攻击者通过 SIM 卡交换攻击破解。 -
恶意软件可能会窃取动态密码。 -
高级攻击者甚至可能通过社会工程学手段获取验证码。
-
在了解了概念后,我们看一下我们常用的一个 MFA 验证应用 Google Authenticator 的实现原理。
6. Google Authenticator 的实现原理
在使用 Google Authenticator 进行 2FA 的过程中,验证的过程可以分为以下两个主要阶段:初始化阶段 和验证阶段。
6.1 初始化阶段:共享密钥生成与分发
这是用户首次启用双因素身份验证时发生的过程。在此阶段,服务端生成共享密钥(Secret Key)并通过安全的方式分发给用户的 Google Authenticator 应用。
-
服务端生成共享密钥:
-
服务端为用户生成一个随机的共享密钥 K
(通常是 16~32 个字符的 Base32 编码字符串,例如JBSWY3DPEHPK3PXP
)。 -
该密钥会作为后续动态密码生成的核心,必须对外保密。
-
-
生成二维码:
-
Example
: 服务提供方的名称。 -
username@example.com
: 用户的账户。在 github 的场景中这个字段是没有的。 -
SECRET=JBSWY3DPEHPK3PXP
: 共享密钥。 -
issuer=Example
: 服务提供方名称(用于显示在 Google Authenticator 中)。
-
服务端将共享密钥和其他元信息(如站点名称、用户账户)打包成一个 URL,符合
otpauth://
协议格式,例如:otpauth://totp/Example:username@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
其中:
-
该 URL 会被编码为一个二维码,供用户扫描。
-
-
用户扫描二维码:
-
用户使用 Google Authenticator 应用扫描二维码,应用会解析出共享密钥( K
)以及站点相关信息,并将其安全存储在手机本地。 -
共享密钥在手机端不会传回服务端,所有计算均在本地完成。
-
-
初始化完成:
-
用户的 Google Authenticator 应用现在可以基于共享密钥 K
和当前时间生成动态密码。 -
服务端同时将该共享密钥 K
绑定到用户账户,并妥善保存以便后续验证使用。
-
6.2 验证阶段:动态密码的生成与验证
这是用户登录时的验证过程。在此阶段,客户端和服务端基于相同的共享密钥K
和时间步长计算动态密码,并进行验证。
6.2.1 客户端生成动态密码
-
获取当前时间:
-
Google Authenticator 应用从设备的系统时间中获取当前的 Unix 时间戳(以秒为单位)。
-
-
将时间戳转换为时间步长:
-
将时间戳除以时间步长(通常为 30 秒),并取整: T = floor(currentUnixTime / timeStep)
例如,当前时间是
1697031000
秒,时间步长为 30 秒,则:T = floor(1697031000 / 30) = 56567700
-
-
计算 HMAC-SHA-1 哈希值:
-
Google Authenticator 将时间步长 T
转换为 8 字节的 Big-endian 格式(例如0x00000000056567700
)。 -
使用共享密钥 K
和时间步长T
作为输入,计算 HMAC-SHA-1 哈希值:HMAC = HMAC-SHA-1(K, T)
结果是一个 20 字节(160 位)的哈希值。
-
-
截断哈希值:
-
根据 HMAC 的最后一个字节的低 4 位,确定一个偏移量 offset
。 -
从 HMAC 中偏移量开始,提取连续 4 个字节,生成动态二进制码(Dynamic Binary Code,DBC)。 -
对提取的 4 字节数据按无符号整数格式解释,并将最高位(符号位)置零,确保结果为正整数。
-
-
取模生成动态密码:
-
对动态二进制码取模 10^6
,生成 6 位数字密码:OTP = DBC % 10^6
例如,计算结果为
123456
。
-
-
显示动态密码:
-
Google Authenticator 将生成的 6 位动态密码显示给用户,该密码有效时间为一个时间步长(通常为 30 秒)。
-
6.2.3 服务端验证动态密码
-
服务端获取当前时间:
-
服务端同样获取当前的 Unix 时间戳,并计算对应的时间步长 T
。
-
-
计算候选动态密码:
-
服务端使用用户账户绑定的共享密钥 K
和当前时间步长T
,通过与客户端相同的 TOTP 算法计算动态密码。 -
为了容忍客户端和服务端的时间差异,服务端通常会计算当前时间步长 T
以及前后几个时间步长(例如T-1
和T+1
)的动态密码,形成候选密码列表。
-
-
验证动态密码:
-
如果匹配成功,则验证通过,用户被允许登录。 -
如果所有候选密码都不匹配,则验证失败,拒绝用户登录。
-
服务端将用户提交的动态密码与候选密码列表逐一比对:
-
6.3 关键数据的传递过程
在整个验证过程中,关键数据的传递和使用如下:
6.3.1初始化阶段
-
服务端 → 客户端: -
共享密钥(K):通过二维码或手动输入传递给 Google Authenticator。 -
站点信息:站点名称、账户名等信息也通过二维码传递。
-
6.3.2验证阶段
-
客户端:
-
本地保存的共享密钥 K
和当前时间计算动态密码。 -
用户将动态密码(6 位数字)手动输入到登录页面。
-
-
客户端 → 服务端:
-
用户提交动态密码(6 位数字)和其他常规登录凭据(如用户名、密码)。
-
-
服务端:
-
使用同样的共享密钥 K
和时间步长计算候选动态密码。 -
对比用户提交的动态密码与计算结果,完成验证。
-
整个过程有如下的一些关键点:
-
共享密钥的安全性:
-
共享密钥 K
是整个验证过程的核心,必须在初始化阶段通过安全的方式传递,并在客户端和服务端妥善保存。 -
密钥不会在验证阶段传输,只有动态密码被提交。
-
-
时间同步:
-
客户端和服务端的时间必须保持同步,否则计算的时间步长 T
会不一致,导致动态密码验证失败。 -
为了适应设备的时间漂移,服务端通常允许一定的时间步长偏移(如 ±1 步长)。
-
-
动态密码的短生命周期:
-
动态密码的有效时间通常为一个时间步长(30 秒),即使密码被窃取,也很快失效。
-
-
离线生成:
-
动态密码的生成完全依赖共享密钥和时间,无需网络连接,增强了安全性。
-
7. 小结
通过 GitHub 强制推行 MFA 的案例,我们可以清晰地看到,MFA 已经成为现代身份管理的重要基石。密码本身的弱点让账户安全长期处于威胁之下,而 MFA 的引入不仅为用户增加了一层甚至多层防护,更在技术上为身份验证树立了一个全新的标准。
尽管 MFA 并非完美,还存在用户体验、实施成本和一定的攻击风险,但它在密码安全性危机中提供了一种强有力的解决方案。无论是个人用户还是企业,采用 MFA 已经成为抵御网络威胁的必要手段。
未来,随着技术的进一步发展,多因子认证可能会越来越多地融合生物特征、行为分析和人工智能技术,为用户提供更安全且更便捷的身份验证体验。而对于每一位开发者和用户来说,理解和使用这些技术,不仅是保护自身数字资产的关键,更是应对日益复杂的网络安全形势的必修课。
以上。