‹ 指南

SSH 两步验证登录 v26.1.6+

有些服务器在密码之后还要 6 位验证码。密钥填一次,之后由 Term 替你回答。

需要 Term v26.1.6 或更高版本。

回答验证码这件事从 v26.1.4 起就能用了。v26.1.6 改的是不成功的时候:告诉你是哪一种失败,以及查清楚这件事要花多少次尝试。

撰写于 · 更新于

它做什么

有些服务器不接受单独的密码:输完密码后还会要一个验证码,也就是验证器 App 上那 6 位数字。这在 SSH 里是另一种认证方式——服务器一个一个地提问、等你回答——所以只会发送密码的客户端根本登不进去。

把你交给验证器 App 的那把密钥同样交给 Term,它就会自己回答这些提问:先密码,再当场生成验证码。

目标主机可以,它经过的跳板机同样可以。两跳是两次独立的登录、各有各的凭据,所以启用了两步验证的跳板机会用它自己的密钥来回答——这正是 v26.1.4 之前连不上的那种情况。

怎么设置

  1. 打开主机——在列表里长按,选「编辑」,展开进阶选项。
  2. 填入密钥。粘贴你在服务器上启用两步验证时看到的那串 base32 密钥,或点二维码按钮,扫描你本来会用验证器 App 扫的同一个码。以 otpauth:// 开头的整条链接也可以直接粘——Term 会从中取出密钥。
  3. 保存并连接。其余照旧:不会弹出任何提问,验证码在连接过程中生成并发送。
主机编辑页里的两步验证密钥字段,右侧带扫码按钮。
字段在「进阶选项」里。二维码按钮会打开扫描器,可以直接扫另一块屏幕上的设置页。

哪些能用

Term 生成的是标准 TOTP——RFC 6238,6 位数字,每 30 秒一换。所以问题不在于你用哪个 App,而在于你的服务器拿什么来校验。下表前两行是为这篇文章在真实服务器上实测过的,第三行则是标准本身的推论。

服务器用什么校验验证码是否可用
pam_google_authenticator✓ 已实测Linux 上最常见的方案,绝大多数「给 SSH 开启两步验证」的教程装的就是它。
pam_oath (oath-toolkit)✓ 已实测oath-toolkit 提供的模块,是另一套独立实现。密钥相同,提问的措辞不同。
privacyIDEA · LinOTP · Authelia✓ 标准 TOTP任何校验标准 TOTP 的实现。这里没有实测,但对我们而言它们并无特别之处。
Duo— 不可用Duo Mobile 的口令是基于计数器的,而常规流程是推送通知。两者都不是 Term 能生成的 TOTP 验证码。
RSA SecurID— 不可用SecurID 用的是它自己的算法,不是 RFC 6238。
SMS · email codes— 不可用验证码是通过另一条渠道发给你的,主机条目里没有任何东西能生成它。

你原本在用的验证器 App——Google 验证器、Authy、1Password、Aegis、FreeOTP——并不需要被「支持」。它们持有的是同一把标准密钥,把这把密钥也给 Term,只是多了一个持有者。另一个请留着。装着 Term 的手机若丢失或被抹掉,上面的 App 也一起没了,还能进得去靠的就是第二个持有者。

不成功时

主机编辑页拒绝了一串含有数字 0 的密钥。
密钥是 base32:只有字母 A–Z 和数字 2–7。0、1、8、9 不在其中——而这几个恰恰是照着屏幕抄时最容易替换掉 O、I、B、g 的字符。现在 Term 会当场指出来,而不是等到登录失败。

它说验证码或密码被拒绝

这条提示比过去更窄,而且是刻意的。Term 只交出了密码和验证码这两个答案,所以用户名和密钥都不在嫌疑之内——而 v26.1.6 之前这里写的是「请检查用户名、密码或密钥」,三样都点了名,还把最不可能的那样放在最前面。

如果密码没错,剩下的就是密钥或者时钟。验证码是由当前时间推出来的,本机时间偏差超过一分钟左右,算出来的码服务器就不会接受:症状和密钥填错一样,原因完全不同。

Term 提示验证码或密码被拒绝,错误码 E_AUTH_CODE
服务器不接受这个验证码。标题点的是 Term 实际交出的那两个答案,并把你引向密钥和时钟,而不是从来就没被怀疑过的用户名。

本来能用,忽然就不行了

多数服务器会给验证码限流——常见默认是 30 秒内三次——一旦触发,连正确的码也会被拒。请等半分钟再试,而不是立刻重来。

从 v26.1.6 起,点一次要花的尝试次数比以前少:Term 不再一上来就用服务器没提供的方式,也不再在无从回答时硬塞一个答案。如果服务器还是在登录中途放弃了,你会看到「服务器在给出结果之前就中断了登录」,而不是一条把责任推给你密码的失败提示。

提问既不是密码也不是验证码

Term 现在读的是整个提问,而不只是它的最后一行。服务器可以把能读的字放在请求的标题或说明里,只留一个光秃秃的标记当提示——现实中就有这么一台,问的是 Two-Step Vertification… / Please Input Mfa Code / (AliyunOTP):,连服务器自己拼错的字都照原样。这一种现在认得出来。如果你那台还是认不出,把原话告诉我们,就能让它被认出来。

它说这台主机需要验证码

服务器要了验证码,而这条主机记录里没有密钥,也就无从回答。Term 会就此停下,不会送出空的或编出来的验证码:错的答案会被计入服务器的尝试次数,在会限流的主机上,这正是把登录锁死的方式。请在高级选项里补上密钥。

如果提示说的是密钥读不出来,那是密钥存着但不是合法的 base32——重新粘一次,并留意上面关于 0、1、8、9 的那段。

Term 提示这台主机需要两步验证码、但没有存密钥,错误码 E_AUTH_2FA
主机要验证码,而它没存密钥。什么都不会送出去——不送空码,也不瞎猜——所以服务器的尝试次数一次都没花。而编辑主机就在旁边,密钥就填在那里。

密钥存在哪里

密钥和主机条目存在一起,位于 App 自己的数据库中——静态加密,放在只有 Term 能读取的沙箱里,除了它生成的那 6 位数字之外不会离开设备。它不是一份可以被拷走的文件;手机锁着时也交不出来:那份存储要等设备本身解锁之后才会解密。

所以真正守着它的是设备锁——也正是那把已经在守着此主机密码的锁,密码就存在同一个数据库里。Term 还可以再问一次:设置里的应用锁,会要求通过设备验证才能打开 App。

另外,请留着你原来的验证器 App。不是为了更安全,而是为了找得回来:手机丢失或被抹掉时,上面的 App 也一起没了,而同一把密钥的第二个持有者,就是你还能进得去的原因。