让 SSH 会话经过一个你不自己运营的 VPN,密码仍然属于 SSH 服务器,而不属于中间的网络。如果通往已验证主机的传输得到了妥善保护,这条路径上的被动观察者就拿不到可读形式的密码。但只要改变一个条件,答案就会改变:未验证的主机、被削弱过的密码套件列表,或被入侵的工作站,都会带来不同的结果。请按顺序执行下面的检查,并把每一项都当成停止条件。

在输入任何内容之前,先说出接收方是谁

密码字段在一条认证消息内部传输,而这条消息又在加密传输内部传输。机密性是传输的属性,也就是说,它取决于是否协商了保护,以及是否与你想连接的那台主机协商了保护。人们跳过的正是后半部分。一条加密到冒名者的连接既机密又毫无用处。VPN 登录不能证明 SSH 主机的任何事,服务商的名声也不能代替密钥核查。

把两个方向在脑子里分开。主机密钥回答你到达了哪台服务器。密码或密钥认证回答服务器接受哪个用户。改进后者永远修不好前者。

核查主机身份、记录与协商结果

从客户端上你已有的记录开始。把已公布的指纹与可信来源比对,再加上仍然匹配的 known_hosts 条目,就得到认证开始前唯一重要的证据。

ssh-keygen -F ssh.internal.example.net -f ~/.ssh/known_hosts
ssh -o StrictHostKeyChecking=yes ssh.internal.example.net

在服务器一侧,把你打算公布的指纹打印出来,而不是凭记忆抄写。把它们放到带外分发才是关键;从与主机名相同的聊天渠道粘贴来的指纹,只能证明两者来自同一个地方。

ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
sudo sshd -T | grep -E 'hostkey|kexalgorithms|cipher'

协商出的算法来自双方的交叉部分。如果客户端配置为了连上旧设备而钉住过时的密钥交换,这个选择会作用于它通信的每一台主机,所以要把它限定在那台主机上,而不是全局生效。

读懂服务器真正接受的方法

知道服务器允许哪些认证方法,就能明白密码究竟是为什么而存在。只接受公钥的主机没有需要保护的密码交换,而仍然接受密码的主机值得一个经过考虑的决定,而不是继承来的默认值。

sudo sshd -T | grep -E 'passwordauthentication|pubkeyauthentication|permitrootlogin'

把某个用户改成密钥认证,会把该用户的密码从那次交换中去掉。这既不会让被入侵的工作站变安全,也不会让不可信的远程会话变得无害。它们是不同的风险,需要不同的控制。还要对代理转发做出决定。把代理转发给一台不可信的主机,等于在会话期间把使用你密钥的能力交给它,这比最初那个密码问题暴露得更多。

把主机密钥警告当作事件步骤来处理

当客户端警告主机标识发生变化时,停下来,并通过不经过同一网络路径的渠道去核实。把指纹与服务器控制台或重建它的人比对。为了让警告闭嘴而删除旧条目,是唯一应当拒绝的动作。它丢弃了证据,把一次可能的中间人拦截变成一次普通登录。

如果主机是合法重建的,只有在新指纹得到确认之后才删除陈旧条目,然后重新连接并再次检查协商。把这次变更连同日期和原因记录下来,因为下一个人会看到同样的警告,需要知道它已经处理完毕。

这份手册没有覆盖的内容

监视流量的 VPN 运营者,与在你机器上拥有本地权限的软件不是一回事。端点被入侵、恶意浏览器扩展或键盘记录器,都位于 SSH 能协商的一切之下,任何隧道都改变不了这一点。如果工作站可疑,请走事件流程,而不是换一个出口重连来测试保密性。重连只能证明网络路径变了,什么也证明不了端点。

最后回到普通情形:已验证的主机、受保护的传输、你故意选择的认证方式,以及没有多余的转发。这个组合就是标题里那个问题的答案,而它是四个决定,不是一个。

相关文章