多因素认证¶
HOTP: HMAC-Based One-time Password
TOTP: Time-based One-time Password
MFA: 多因素认证(Multi-factor authentication
多因素认证(Multi-factor authentication,缩写为 MFA)是一种安全系统,是为了验证一项交易的合理性而实行多种身份验证。MFA 的目的是建立一个多层次的防御,使未经授权的人访问计算机系统或网络更加困难。
MFA 是通过结合两个或三个独立的凭证:
用户知道什么(知识型的身份验证)
用户有什么(安全性令牌或者智能卡)
用户是什么(生物识别验证)
而单因素身份验证(SFA)与之相比,只需要用户现有的知识。
虚拟 MFA 设备
是能产生 6 位数字认证码的应用程序,遵循基于时间的一次性密码 (TOTP)标准(RFC 6238)。此类应用程序可在移动硬件设备(包括智能手机)上运行,而且这个应用程序不需要连接网络即可工作。
2FA¶
2FA 的简称是两步验证,它是一种安全措施,要求用户在输入密码之后再提供另外一种验证方式来证明其身份,以确保账户的安全性。2FA 通常需要用户提供一个额外的因素来验证身份,例如:指纹、短信验证码、令牌生成器、硬件安全密钥等等。这种额外的身份验证步骤增加了账户的安全性,因为即使密码被泄露,攻击者也需要提供另一种验证因素才能访问用户的账户。
HOTP¶
HOTP 的工作原理如下:
客户端和服务器事先协商好一个密钥 K,用于一次性密码的生成过程,此密钥不被任何第三方所知道。
此外,客户端和服务器各有一个计数器 C,并且事先将计数值同步。
进行验证时,客户端对密钥和计数器的组合 (K,C) 使用 HMAC算法计算一次性密码
公式如下:
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
说明:
HMAC 算法得出的值位数比较多,不方便用户输入,因此需要截断(Truncate)成为一组不太长十进制数(例如 6 位)
计算完成之后客户端计数器 C 计数值加 1。
用户将这一组十进制数输入并且提交之后,服务器端同样的计算,并且与用户提交的数值比较
如果相同,则验证通过,服务器端将计数值 C 增加 1。
如果不相同,则验证失败。
备注
这儿有个问题。如果验证失败或者客户端不小心多进行了一次生成密码操作,那么服务器和客户端之间的计数器 C 将不再同步,因此需要有一个重新同步(Resynchronization)的机制。详情可以参看 RFC 4226
TOTP¶
介绍完了 HOTP,TOTP也就容易理解了。TOTP 将 HOTP 中的计数器 C 用当前时间 T 来替代,于是就得到了随着时间变化的一次性密码。
问题一:时间 T 的值怎么选取:
因为时间每时每刻都在变化,如果选择一个变化太快的 T(秒),那么用户来不及输入密码。
如果选择一个变化太慢的 T(小时),那么第三方攻击者就有充足的时间去尝试所有可能的一次性密码
(6 位数字的一次性密码仅仅有 10^6 种组合),降低了密码的安全性。
除此之外,变化太慢的 T 还会导致另一个问题:
如果用户需要在短时间内两次登录账户,由于密码是一次性的不可重用,用户必须等到下一个一次性密码被生成时
这意味着T(小时)最多需要等待 59 分 59 秒!这显然不可接受
综合以上考虑,Google 选择了 30 秒作为时间片
T 的数值为从 Unix epoch(1970 年 1 月 1 日 00:00:00)来经历的 30 秒的个数
问题二:网络延时等,导致验证经常失败:
由于网络延时,用户输入延迟等因素,可能当服务器端接收到一次性密码时,T 的数值已经改变
这样就会导致服务器计算的一次性密码值与用户输入的不同,验证失败。
解决这个问题个一个方法是:
服务器计算当前时间片以及前面的 n 个时间片内的一次性密码值,只要其中有一个与用户输入的密码相同,则验证通过。
当然,n 不能太大,否则会降低安全性,通常这个 n 被设置为 3。
操作流程¶
实现 Google Authenticator 功能需要服务器端和客户端的支持:
服务器端负责密钥的生成、验证一次性密码是否正确。
客户端记录密钥后生成一次性密码。
步骤一:开启 Google Authenticator 服务时:
1. 服务器随机生成一个类似于『xxxxxxxxxxxx』的密钥,并且把这个密钥保存在数据库中。
2. 在页面上显示一个二维码,内容是一个 URI 地址(otpauth://totp/ 发行方:账号?secret = 密钥),如:
『otpauth://totp/issuer:accountName?secret=xxxxxxxxxxxxx』
3. 客户端扫描二维码,把密钥和用户名及发行方保存在客户端。
步骤二:用户登陆时:
1. 客户端每 30 秒使用密钥和时间戳通过一种『算法』生成一个 6 位数字的一次性密码
2. 用户登陆时输入一次性的 6 位数密码
3. 服务器端使用保存在数据库中的密钥『xxxxxxxxxxxx』和时间戳通过同一种『算法』生成一个 6 位数字的一次性密码
如果算法相同、密钥相同,又是同一个时间(时间戳相同),那么客户端和服务器计算出的一次性密码是一样的
服务器验证时如果一样,就登录成功了
相关链接¶
下载地址: https://github.com/google/google-authenticator-android/releases
JAVA 版本的一个实现: https://github.com/wstrange/GoogleAuth
PHP 版本的一个实现: https://github.com/PHPGangsta/GoogleAuthenticator
PAM Module: https://github.com/google/google-authenticator-libpam:
The PAM module can add a two-factor authentication step to any PAM-enabled application. It supports: Per-user secret and status file stored in user's home directory Support for 30-second TOTP codes Support for emergency scratch codes Protection against replay attacks Key provisioning via display of QR code Manual key entry of RFC 4648 base32 key strings
使用 Google Authenticator 实现两步验证加固 SSH 安全: https://cloud.tencent.com/developer/article/1444296