基于TOTP算法进行服务器双因素SSH登录的安全指南
特别注意
请您知悉本文实质为应用教程。此外代码高亮存在问题,敬请谅解。第一次撰写技术类文章,代表了博客开启了新的阶段。祝您阅读愉快!

基于TOTP算法进行服务器双因素SSH登录的安全指南

  要:谷歌身份验证器 (Google Authenticator) 是 Google 推出的基于时间的一次性密码 (Time-based One-time Password, TOTP) 算法,其与 SSH Password 或 Public Key 配合,在普通 SSH 密码或密钥验证前进行基于 TOTP 的二阶段验证,以提升 SSH 登录安全性。

关键词:双因素认证,基于时间的一次性密码,服务器安全,SSH

Security guidelines for server’s two-factor SSH login based on the TOTP algorithm

Abstract: Google Authenticator is Google’s Time-based One-time Password (TOTP) algorithm, which works with SSH Password or Public Key. SSH security is enhanced by TOTP two-phase authentication after SSH Password or Public Key authentication.

Key words: 2FA, TOTP, Machine Security, SSH


1 引言

本文将详细讲述通过谷歌身份验证器(Google Authenticaator)基于时间的一次性密码算法(Time-based One-time Password, TOTP),其与SSH Password 或Public Key 配合,在普通SSH密码或密钥验证前进行基于TOTP的二阶段验证(登陆窗口提示输入 Verification Code),以提升SSH登录安全性。

2 双因素身份认证(2FA)介绍

2.1 双因素认证概念

一般情况下,三种不同类型的证据,可以证明一个人的身份。

  • 秘密信息:只有该用户知道、其他人不知道的某种信息,比如密码。
  • 个人物品:该用户的私人物品,比如身份证、钥匙。
  • 生理特征:该用户的遗传特征,比如指纹、相貌、虹膜等等。

这些证据被称为三种“因素”(factor)。因素越多,证明力越强,身份的可靠性越高。而双因素认证就是指,通过两种因素进行认证。

双因素认证的模式被广泛应用于日常生活中。其中银行卡就是最普遍的双因素认证,在传统方式下,用户只有通过提供银行卡(个人物品)和密码(秘密信息),才能取出现金。除此之外还有火车站检票进站(通过身份证+人脸)等诸多场景也使用了双因素认证。

2.2 双因素认证常见的不同组合方案

2.2.1 密码+物理密钥

最常见的双因素组合就是密码+U盾的网银登录方式,用户通过插入U盾,再输入密码,从而登录网上银行。

但是,U盾这一物品的特殊性让其无法被用户随身携带。因此,密码+手机的组合成为了最佳的双因素认证方案。

图1 Google 推出的 Titan物理密钥

2.2.2 密码+手机

该组合最常见的验证方式即在用户输入密码的同时,输入手机短信收到的密码,用以证明用户身份的真实性。然而由于通过GSM协议传输的短信为明文传输,造成了短信容易被拦截和伪造的风险,除此之外,还存在克隆SIM卡等风险。

由此看来,密码+短信验证码并非是最佳的双因素认证方案。而下文的TOTP,可有效规避其中的安全风险。

图2 基于 SMS 验证码的2FA验证

3 TOTP双因素认证介绍

3.1 TOTP的概念

基于时间的一次性密码算法(TOTP)是一种根据预共享的密钥与当前时间计算一次性密码的算法。它已被互联网工程任务组接纳为RFC 6238标准,成为主动开放认证(OATH)的基石,并被用于众多多重要素验证系统当中。

TOTP是散列消息认证码(HMAC)当中的一个例子。它结合一个私钥与当前时间戳,使用一个密码散列函数来生成一次性密码。由于网络延迟与时钟不同步可能导致密码接收者不得不尝试多次遇到正确的时间来进行身份验证,时间戳通常以30秒为间隔,从而避免反复尝试。

在特定的多重因素验证应用中,用户验证步骤如下:一位用户在网站或其他服务器上输入用户名和密码,使用运行在本地的智能手机或其他设备中的TOTP生成一个一次性密码提交给服务器,并同时向服务器输入该一次性密码。服务器随即运行TOTP并验证输入的一次性密码。为此,用户设备与服务器中的时钟必须大致同步(服务器一般会接受客户端时间-1区间(也就是延迟了30秒)的时间戳生成的一次性密码)。在此之前,服务器与用户的设备必须通过一个安全的信道共享一个密钥,用于此后所有的身份验证会话。如需要执行更多步骤,用户也可以用TOTP验证服务器。

3.2 TOTP的应用步骤

第一步:用户在服务端配置并启用双因素认证,服务器生成预共享密钥;

第二步:用户根据服务器提示通过扫描二维码等方式将密钥保存至手机;

第三步:用户登陆时,手机客户端使用该密钥与当前时间戳,生成一串哈希,有效期默认为30秒。用户在有效期内,将哈希提交给服务器;

第四步:服务器也同时使用该密钥和服务器当前时间戳,生成一串哈希,与用户提交的哈希进行比对,二者一致则同意登录;反之则拒绝登录。

图3 Microsoft Authenticator

3.3 TOTP的算法

那么,客户端与服务端,如何保证在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

4 实现基于TOTP的二步验证SSH

4.1 基本概念

服务端:计算机系统、超算系统等,需要用此认证登录的对应系统。

安全密钥:Secret Key,用于通过手动输入注册浏览器插件或手机客户端等使用,如果泄露,请立即在服务端上重新生成并重新关联到浏览器插件或手机客户端。

二维码:QR Code,用于通过扫描注册浏览器插件或手机客户端等使用,如果泄露,请立即在服务端上重新生成并重新关联到浏览器插件或手机客户端。

紧急安全码:Emergency Scratch Code,格式为八位数字,在身份验证器的动态验证码不可用时需要登录服务端时使用,如手机不在身边,请将紧急安全码记录到安全的地方,这些数字是一次性使用的登录密码,使用后自动作废。

验证码、动态验证码、口令牌:Verification code或TOKEN,格式为六位数字,用户登录系统时使用。

刷新周期:默认为30秒,即每30秒动态验证码更新一次。

窗口大小:Window Size,指的是可用于登录服务端的当前时间前后刷新周期数(验证码数),比如17表示当前时间前后各8个,当前时间前后各8*30秒(4分钟)时间段内生成的验证码都可用于此次服务端登录。

配置文件:个人的 ~/.google_authenticator 文件,里面含有安全密钥、紧急安全码等重要信息,不要泄露。该文件不得删除,删除讲导致无法登录;权限不要动(他人不得具有此文件的读权限等),有可能会导致无法登录。

4.2安装步骤

4.2.1 步骤一-安装 Google’s PAM

更新 Ubuntu 仓库缓存:

$ sudo apt-get update

安装PAM:

$ sudo apt-get install libpam-google-authenticator

运行初始化应用程序:

$ google-authenticator

您希望身份验证令牌是基于时间的吗?回复 y

Output
Do you want authentication tokens to be time-based (y/n) 

此时屏幕上将输出许多信息,包括一个二维码。此时,使用您手机上的认证应用程序来扫描二维码或手动键入密钥。如果二维码太大而无法扫描,您可以使用二维码上方的网址来获得一个更小的版本。

注意:请保证 Shell 窗口足够大以正常完整显示二维码!

是否更新认证文件?回复 y。

如果选择 n,程序将退出并且不写入任何内容,这意味着认证程序不能工作。

Output
Do you want me to update your "~/.google_authenticator" file (y/n)

是否要禁止多次使用同一代码认证?这限制了您每30秒只能登录一次,但是增加了您注意到甚至阻止中间人攻击的机会。回复 y

通过在这里回答 y,可以使每个代码在使用后立即过期,从而防止重播攻击。这可以防止攻击者获得刚刚使用的代码并随之登录。

Output
Do you want to disallow multiple uses of the same authentication token? This restricts you to one login about every 30s, but it increases your chances to notice or even prevent man-in-the-middle attacks (y/n) 

默认情况下,代码有效期为30秒。由于时间可能不同步,可以将代码有效时间延长至4分钟 建议回复 n

Output
By default, tokens are good for 30 seconds and in order to compensate for possible time-skew between the client and the server, we allow an extra token before and after the current time. If you experience problems with poor time synchronization, you can increase the window from its default size of 1:30min to about 4min. Do you want to do so (y/n) 

限制尝试次数,每30秒最大可尝试3次,回复 y 启用。

Output
If the computer that you are logging into isn't hardened against brute-forcelogin attempts, you can enable rate-limiting for the authentication module.By default, this limits attackers to no more than 3 login attempts every 30s.Do you want to enable rate-limiting (y/n)

注意: 一旦您完成了这个设置,如果您想备份您的密钥,您可以复制 ~/.google-authenticator 文件。谷歌认证文件到一个可信的位置。由此,您可以将它部署到其他系统上,或者在备份之后重新部署它。

现在已经安装和配置了 Google 的 PAM,下一步是配置 SSH 以使用 TOTP 密钥。我们需要告诉 SSH 有关 PAM 的信息,然后配置 SSH 使用它。

4.2.2 步骤二-配置 OpenSSH

编辑 sshd 文件:

$ nano /etc/pam.d/sshd

在文件底部添加以下内容:

# Standard Un*x password updating.
@include common-password
Auth required pam_google_authenticator.so nullok

nullok 最后一行末尾的单词告诉 PAM 这种身份验证方法是可选的。这允许没有 OATH-TOTP 令牌的用户仍然使用他们的 SSH 密钥登录。一旦所有用户都拥有 OATH-TOTP 令牌,您就可以 nullok 从此行中删除以强制使用MFA。

保存并关闭文件。

配置SSH支持MFA认证:

$ nano /etc/ssh/sshd_config

在文件中查找 ChallengeResponseAuthentication 并将其设置为 yes

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes

保存并关闭文件。

重新启动sshd服务来重新加载配置。

注意:不要关闭当前shell,否则您将因配置错误无法登录服务器!

$ systemctl restart sshd.service

请打开另一个终端并尝试通过 SSH 登录。如果您以前已经创建了一个 SSH 密钥并且正在使用它,此时您将不必输入密码或 MFA 验证码。因为在默认情况下,SSH 密钥会覆盖所有其他身份验证选项。否则,您将看到输入密码和验证码提示。

4.2.3 步骤三-配置 SSH 两步验证

重新打开 sshd 配置文件:

$ nano /etc/ssh/sshd_config

在文件底部添加以下内容,此后在SSH 登录时需要使用 SSH 密钥+验证码:

UsePAM yes
AuthenticationMethods publickey,keyboard-interactive

此处可自选填入:

publickey,keyboard-interactive #使用SSH密钥+验证码登录
publickey #仅使用SSH密钥登录
keyboard-interactive #仅使用验证码登录

需要注意的是,后两种选择并不能达到2FA的最初目的,请谨慎选择。

值得一提的是,互联网上其他教程中所展示的publickey,password 组合方案经实测是无法正常登录的,请注意合理选择。

保存并关闭文件。

再次打开 PAM sshd 配置文件:

$ nano /etc/pam.d/sshd

找到 @include common-auth 在句首添加 # ,此举告诉服务器不要提示输入密码。

# Standard Un*x authentication.
#@include common-auth

保存并关闭文件。

重新启动sshd服务来重新加载配置。 注意:不要关闭当前shell,否则您将因配置错误无法登录服务器!

$ systemctl restart sshd.service

请打开另一个终端并尝试通过 SSH 登录。如果符合您的预期设置,那么配置成功!

图4 Xshell 7 SSH登录 验证码输入窗口

4.2.4 步骤四-添加三步验证(可选)

在我们已经成功在 sshd_config 文件中添加并允许了以下认证方式:

  • 密码
  • SSH密钥
  • 验证码

尽管我们允许了这三项认证方式,但是现在只能通过 SSH 密钥和(或)验证码来连接到服务器,如果您希望同时启用上面的三种认证方式,修改 PAM 目录下的 sshd 配置文件,将之前在 @include common-auth 前面添加的 # 删掉,并保存再重新加载 SSH 服务即可。

如此一来,您的认证步骤将会如下所示:

publickey,keyboard-interactive # SSH密钥+密码+验证码
publickey # SSH密钥(无法实现SSH密钥+密码)
keyboard-interactive #密码+验证码
图5 Xshell 7 连接的用户身份验证设置

4.3 恢复访问权限

4.3.1 失去对TOTP APP的访问

您可通过在初次创建时系统自动生成的紧急安全码登录服务器。需要注意的是,每一个紧急安全码仅可使用一次。

4.3.2 丢失SSH密钥或TOTP密钥

如果丢失了 SSH 密钥或 TOTP密钥,恢复SSH登录可以分为几个步骤。第一步是在不知道动态验证码的情况下重新登录,第二步是为MFA登录找到TOTP密钥或重新生成。

若您拥有SSH密钥且通过SSH密钥登录SSH时无需提供MFA动态验证码,您可以轻易通过SSH密钥登录SSH。此外您还可以通过其他具有sudo访问权限的用户或VPS虚拟控制台进行登录。

一旦您成功登录,您有两种方法获得TOTP密钥:

  1. ~/.google-authenticator 中重新取得TOTP密钥;
  2. 生成一个新密钥。

在每个用户的主目录中,TOTP密钥和 Google Authenticator 设置保存在 ~/.google-authenticator 中,该文件的第一行是密钥。

您也可以删除 ~/.google-authenticator,这允许用户只使用一个因素再次登录,然后您可以运行 Google Authenticator 来生成一个新的密钥。

4.4 进阶操作

如果您想在初始配置之后更改 MFA 设置,您可以只编辑 ~/,而不是使用更新后的设置生成新的配置。谷歌认证文件。该文件按以下方式编排:

<secret key>
<options>
<recovery codes>

以下是您可以对这个文件所做的修改:

  • 若要启用顺序验证码而非基于时间的验证码,请更改 “ TOTP_AUTH 为 “ HOTP_COUNTER 1。
  • 若要允许多次使用同一验证码,请删除 “ DISALLOW_REUSE。
  • 若要将验证码过期窗口延长至4分钟,请添加 “ WINDOW_SIZE 17。
  • 若要禁用多次失败的登录,请删除 “ RATE_LIMIT 3 30。
  • 若要更改速率限制阈值,请找到 “ RATE_LIMIT 3 30 并调整数字。3 表示一段时间内尝试的次数,30 表示以秒为单位的时间周期。
  • 若要禁用紧急安全码,请删除文件底部的5个8位代码。

5 总结

综上可知,通过在两个实体(您的计算机 + 您的手机)上拥有两个因素(SSH 密钥/密码 + MFA 令牌) ,外部代理很难通过 SSH 强行进入您的服务器,并大大提高了服务器的安全性。

然而,缺点在于,登录多了一步,费时且麻烦,用户会感到不耐烦。而且,它也不意味着账户的绝对安全,入侵者依然可以通过盗取 cookie 或 token,劫持整个对话(session)。且一旦忘记密码或者遗失手机,想要恢复登录,势必就要绕过双因素认证,这就形成了一个安全漏洞。除非准备两套双因素认证,一套用来登录,另一套用来恢复账户。

图6 会话劫持原理图

6 参考资料

[1]中国科学技术大学超级计算中心.基于Google Authenticator二阶段密码验证SSH登录用户端用法[EB/OL].https://scc.ustc.edu.cn/2018/0926/c409a339006/page.htm,2018-09-26.

[2]维基百科.基于时间的一次性密码算法[EB/OL].https://zh.wikipedia.org/wiki/基于时间的一次性密码算法,2021-01-20.

[3]阮一峰的网络日志.双因素认证(2FA)教程[EB/OL].https://www.ruanyifeng.com/blog/2017/11/2fa-tutorial.html,2017-11-02.

[4]DigitalOcean.How To Set Up Multi-Factor Authentication for SSH on Ubuntu 16.04[EB/OL].https://www.digitalocean.com/community/tutorials/how-to-set-up-multi-factor-authentication-for-ssh-on-ubuntu-16-04,2017-01-10.

7 PDF下载

N/A

本文作者:qiuyuair
本文链接:https://qiuyuair.com/server-two-factor-ssh-login-based-on-the-totp-algorithm_20210331/
版权声明:本站采用 BY-NC-SA 进行许可。转载请注明出处!

评论

  1. gwisdoms
    Windows Chrome 90.0.4430.212
    3 年前
    2021-7-01 17:53:55

    博主,您好,我最近也在看这个双因子认证的方案,有个问题想跟您请教:通过xshell -url ssh://username:password@ip:port 的这种方式密码验证是失败的,不知道您是否有了解过

    • 博主
      gwisdoms
      Windows Chrome 91.0.4472.124
      3 年前
      2021-7-02 0:29:59

      您好!关于这个问题,我暂时没有查询到相关解决方法,通过实践也暂时无法实现您所说的需求。通过查询资料可知,基于键盘交互的验证方式(keyboard interactive authentication method),是通过服务器向客户端发送提示信息,然后由客户端根据相应的信息通过手工输入的方式发还给服务器端。因此直接通过您所说方式进行验证很可能是不可行的,此外,似乎也无法用 ssh:// 使用密钥验证。综上所述,在我看来,您可以姑且使用 ssh://username@host 的方式进行连接,再根据终端中的提示输入 verification code ,也许这是一种变通的办法。

      • gwisdoms
        qiuyuair
        Windows Chrome 90.0.4430.212
        3 年前
        2021-7-02 9:38:30

        博主您好,可能我之前没有描述清楚,我是有两种方案的,方案一已经实现了。
        方案一:是通过网页端调用本地安装的putty来免密登录linux(秘钥+verification code),但putty用起来不是很友好。
        方案二:xshell是不支持验证码传参的,只支持密码传参。我的意思是web端直接调用本地安装的xshell(传参为username passwd)然后输入verification code即可登录linux
        不知道第二种方案你是否有研究过,或者是第二种方案是否是可行的,因为我在使用xshell -url ssh://username:password@ip:port进行验证的时候密码验证一直是失败的

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇