SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor

SSH历史:从Telnet到OpenSSH及后量子默认设置

1995年赫尔辛基密码嗅探攻击催生SSH。本文按已核实时间线,梳理Telnet、rlogin、SSH到OpenSSH的发展,以及后量子默认设置的演变。

SSH 的历史起点

SSH 的历史始于密码被窃取。在 1995 年之前,登录远程 Unix 计算机通常使用 telnet 或 rlogin,这两者都会以可读文本形式通过网络发送密码。任何能够监视网络线路的人都可以读取密码。到 1990 年代初,人们确实已经在大规模实施这种窃取。

SSH 是一个人针对这一问题提出的解决方案,于 1995 年编写并免费发布。此后,该协议被重写过一次,而如今几乎所有人运行的程序,都是某个分支的再次分支。下面的日期很重要,因为每一步都是针对某个具体故障作出的回应。

Telnet 和 rlogin 实际发送了什么

Telnet 由 RFC 854 定义。该 RFC 由 Jon Postel 和 Joyce Reynolds 编写,于 1983 年 5 月发布。它描述了通过 TCP 传输的终端会话,不包含任何形式的加密。您输入的每个字节(包括密码)都会以明文形式传输,路径上的任何设备都可以读取这些内容。

rlogin 源自 Berkeley Unix,后来在 RFC 1282(BSD Rlogin,B. Kantor,1991 年 12 月)中进行了说明。它引入了比可读密码更严重的问题:基于主机的信任。服务器可以配置为接受来自指定主机的登录,完全不要求密码。该 RFC 包含名为“A Cautionary Tale”的章节,其中写道:“绕过受信任主机的密码身份验证后,只要其中一台主机遭到入侵,所有采用此配置的系统都会暴露。”该 RFC 还指出,这种信任关系基于主机名,因此 DNS(域名系统)遭到入侵或地址被伪造时,这种机制就会失效。

这两种设计都适用于它们诞生时的网络环境。早期 Ethernet 是共享介质:网段上的每台机器都会收到每个帧,并且应忽略发往其他设备的帧。停止忽略这些帧,就是进入混杂模式,此时机器可以看到其他设备的全部网络流量。再加上大学向数千名学生提供 shell 账户,只要一个账户遭到入侵,就可能成为收集整个院系密码的入口。

没有修复方案的 1994 年公告

1994 年 2 月 3 日,CERT 发布了公告 CA-94:01《持续进行的网络监控攻击》。公告称,入侵者已经截获了互联网上数万个系统的访问凭据。他们使用的工具会将网络接口置于混杂模式,并记录每个新建 telnet、rlogin 和 FTP 会话的开头部分;其中包含用户名和密码。

CERT 建议各站点更改所有可通过网络访问的账户密码。结合这些协议来看,问题很明显:新密码首次使用时,仍会以明文形式通过同一条网络链路。telnet 和 rlogin 内部都无法提供修复方案,因为这两个协议都没有可用于实现修复的机制。

Helsinki 的嗅探攻击为何催生了 SSH

1995 年,Helsinki University of Technology 的网络遭到 CERT 描述的密码嗅探攻击。该校研究人员 Tatu Ylönen 编写了替代方案,并于 1995 年 7 月以免费软件形式发布。他将其命名为 Secure Shell。

这项方案依靠两项设计决策得以实现。会话经过加密,因此监听该网段无法获得有用信息。服务器还使用密钥证明自身身份,因此客户端可以判断连接的是否为正确主机。这正是 rlogin 的主机名信任机制留下的漏洞。

它还因为命令与用户已有的输入习惯一致而迅速普及。ssh 代替了 rshrloginscp 代替了 rcp。切换只需改变使用习惯,不会改变工作流程。到 1995 年底,用户数量已达到约 20,000,遍布五十个国家。同年 12 月,Ylönen 创办了 SSH Communications Security,用于开发和销售该软件。

从免费发布版本到商业产品

随着 SSH 成为一项商业业务,其源代码的许可证发生了变化。后续发布版本附带了限制其他人使用代码方式的条款,最后一个任何人都可以自由复用的版本是 ssh 1.2.12。这并没有任何不当之处。它只是意味着,世界其他地方可以在其基础上构建的 SSH 版本停止了发展,而开发工作仍在一个外界无法跟进的地方继续。许可证决定哪些代码能够延续,这一模式值得参阅开放源代码许可证如何塑造现代基础设施

OpenBSD 为何在 1999 年分叉 OpenSSH

1999 年初,Björn Grönvall 回到最后一个免费版本,并开始修复其中的错误。他的版本称为 OSSH,且仅支持 SSH 1.3 协议。

OpenBSD 项目采用了 OSSH,并对其进行了重建。根据该项目自己的说明,Theo de Raadt、Niels Provos、Markus Friedl、Bob Beck、Aaron Campbell 和 Dug Song 负责清理、审计和扩展代码。最终版本为 OpenSSH 1.2.2,并于 1999 年 12 月 1 日随 OpenBSD 2.6 发布。

为什么一个小型操作系统项目的分叉版本最终会出现在几乎每台机器上?原因在于 OpenBSD 对它的需求。OpenBSD 发布经过审计的基础系统,目标是在默认配置下保持安全。因此,加密远程登录必须包含在基础系统中,并采用不附加限制的许可证。经过审计且采用无限制许可证的代码,正是其他所有操作系统供应商也需要的代码。Damien Miller、Philip Hands 等人几乎立即开始维护可移植分支,这就是类似 10.5p1 的版本中出现 p 的原因。OpenBSD 开发干净版本,可移植分支则为其他系统添加适配代码。Unix 演变为如今所用各类系统的过程说明了为什么必须添加这些适配代码。

随后,OpenSSH 增加了对第 2 个协议版本的支持。OpenSSH 2.0 于 2000 年 6 月 15 日随 OpenBSD 2.7 发布。

为什么 SSH-2 是新协议,而不是版本号递增

SSH-1 使用 CRC-32 保护加密流的完整性。CRC-32 原本用于检测传输错误,而不是抵御攻击者。1998 年,CORE SDI 的 Ariel Futoransky 和 Emiliano Kargieman 展示了这会造成什么后果。在 CBC 或 CFB 加密模式以及 CRC-32 校验下,攻击者只需知道明文中少至 16 个字节,就能插入接收方会当作真实数据接受的自选密文,从而在服务器上执行命令。

该漏洞存在于协议本身,因此无法在不破坏兼容性的情况下修复。实现方改为加入检测器,即文件 deattack.c 中用于在攻击发生时识别攻击的代码。2001 年 2 月,有人发现该检测器自身存在整数溢出漏洞 CVE-2001-0144。携带该补丁的服务器和客户端因此可能遭到远程代码执行。无法修复的设计只能不断打补丁,而补丁本身也会引入新的漏洞。

SSH-2 由名为 secsh 的 IETF 工作组制定,并于 2006 年 1 月以 RFC 形式发布:RFC 4251 定义架构,RFC 4253 定义传输层,RFC 4252 定义用户认证,RFC 4254 定义连接层。分层是其中的关键,因为这样可以单独替换每一层。这段历史的大部分内容,实际上就是这些替换逐步发生的过程。

其中有两项变化尤其重要。完整性保护从 CRC-32 改为使用共享密钥的 HMAC(基于哈希的消息认证码),因此无法计算 MAC 的攻击者不能伪造数据包。密钥协商也改为 Diffie-Hellman。在 SSH-1 中,客户端选择会话密钥,并使用服务器的 RSA 密钥加密后发送。因此,任何之后取得这些私钥的人,都可以解密已记录的会话。Diffie-Hellman 为每个会话生成新的秘密值,且该值不会在网络中传输。因此,即使现在记录流量、之后再窃取主机密钥,也无法解密会话。这种属性称为前向保密。

SSH-2 与 SSH-1 不共享线路协议兼容性。因此,版本号改为新的整数,而不是递增小数。

为何移除 SSH-1,而不是修复它

移除过程持续了 3 个 OpenSSH 版本。7.0 版本于 2015 年 8 月 11 日在编译时默认禁用协议 1。7.4 版本于 2016 年 12 月 19 日移除了对该协议的服务器端支持。7.6 版本于 2017 年 10 月 3 日进一步删除了客户端支持,以及相关配置选项和文档。

为旧设备保留可选支持本来会更方便,而 CRC-32 检测器说明了为何最终放弃这一做法。只有在协议 1 代码被编译进去时,才可能触发该溢出;而且这段代码位于大多数管理员认为在其系统上不会运行的路径中。已发布的代码都可能被执行。被删除的代码则无法执行。

首次建立 SSH 连接时为何会收到主机密钥警告

加密只能说明网络流量是私密的,不能说明连接另一端的身份。如果攻击者位于通信路径中,并代替服务器响应请求,您会与攻击者建立完全加密的会话。这称为中间人攻击。SSH 使用主机密钥应对这种攻击:服务器证明自己持有密钥对中的私钥,客户端则将该密钥与上次记录的值进行比对。如果您想了解连接本身的工作机制,请参阅打开 SSH 连接时会发生什么

首次连接时不存在“上次记录”,因此客户端没有可供比较的值,只能询问您:

The authenticity of host 'vps.example.com (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:BQ0Qs2jVXhH2lPZ2rM0aQ0mQ8f9pQ0m3nH0oQ1bYqkE.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

回答 yes 会将该密钥保存到 ~/.ssh/known_hosts。此后的每次连接都会与保存的值进行比较。如果不匹配,程序会显示最严重的警告:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

首次提示的准确含义是:协议承认首次连接存在一个薄弱环节。首次使用时信任意味着,首次连接的安全性取决于建立连接时所使用的网络。您可以缩小这个风险。连接前,从服务提供商的控制台或服务器的构建日志中读取指纹。也可以按照 RFC 4255 将指纹发布为 DNS 中的 SSHFP 记录,但只有在使用 DNSSEC 时才值得这样做。或者,使用您自己的证书颁发机构(CA)为主机密钥签名,使客户端信任 CA,而不是分别信任每个主机密钥。实际上,大多数人都会在未检查的情况下接受提示,对此应当有清醒认识。

公钥如何取代密码

公钥身份验证从早期 SSH 版本开始就已存在,但经过多年才成为常规做法。该机制采用非对称加密:客户端通过对质询进行签名,证明自己持有私钥;私钥始终不会离开客户端。密码则相反。即使 SSH 将密码放在加密通道中传输,服务器仍会收到实际的机密内容。因此,如果服务器已被攻破或由恶意方控制,攻击者就可能获取可在其他服务上重复使用的凭据。

第二个原因在于计算难度。任何在公网地址上开放 22 端口的服务器,都会全天候收到自动化登录尝试,而密码只是可猜测的字符串。从实际角度看,密钥无法被猜中。设置 PasswordAuthentication no 可以彻底阻断这一类攻击,因此它会出现在所有安全加固清单中。密钥的生成和轮换请参阅 SSH 密钥管理基础,服务器端设置请参阅 加固 VPS 上的 SSH

SSH 算法列表为何不断变化

分层协议允许在不推出新协议的情况下淘汰算法。OpenSSH 一直在利用这一机制,发布版本的日期显示了这一变化速度。

Ed25519 随 OpenSSH 6.5 于 30 January 2014 引入,同时加入的还有 chacha20-poly1305 密码和受 bcrypt 保护的私钥格式。Ed25519 签名以确定性方式生成每个签名使用的 nonce,因此签名时使用较弱的随机数生成器也不会泄露私钥。现实中的安全事件已经证明,DSA 和 ECDSA 私钥正是这样被恢复的。

DSA 则走向了另一条路。OpenSSH 7.0 于 2015 年在运行时禁用了 ssh-dss 主机密钥和用户密钥,因为该算法将私钥长度限制为 160-bit,并且只能使用 SHA-1。Version 9.8 于 1 July 2024 在编译时禁用了 DSA。Version 10.0 于 9 April 2025 移除了 DSA,用项目的话说,这是“完成了始于 2015 年的弃用流程”。从禁用到删除,历时十年。

RSA 并未消失,但其旧签名格式消失了。OpenSSH 8.8 于 26 September 2021 起默认不再接受使用 SHA-1 生成的 RSA 签名。发布说明明确说明了原因:SHA-1 在密码学上已经失效,攻击者可以用低于 USD 50,000 的成本实现 chosen-prefix collision。如果你在连接旧服务器时遇到过 sign_and_send_pubkey: no mutual signature supported,那就是这项变更。你的密钥没有问题。对端要求使用的签名算法已经不安全。

同样的流程现在也应用于密钥交换,而且这次是在威胁出现之前进行。今天捕获的流量可以被保存下来,等到拥有高性能量子计算机的一方在多年后解密。因此,密钥协商必须在此类机器出现之前完成变更。OpenSSH 9.0 于 8 April 2022 将混合密钥交换设为默认:sntrup761x25519-sha512@openssh.com 将后量子算法与 X25519 交换结合起来,因此即使新算法存在问题,结果也不会弱于其中的经典部分。OpenSSH 9.9 于 19 September 2024 加入了 mlkem768x25519-sha256,该算法基于 ML-KEM(module lattice key encapsulation mechanism),由 NIST 于 2024 年标准化。OpenSSH 10.0 将其设为密钥协商的默认算法,项目的后量子页面解释了其中的原因。OpenSSH 10.1 于 6 October 2025 开始在对端不支持该算法时发出警告:

** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded.

该警告默认启用,并由 ssh_config 中的 WarnWeakCrypto 选项控制。它在实际环境中的含义,以及如何处理触发该警告的服务器,见后量子 SSH 密钥交换默认设置

您面前的服务器意味着什么

自 1995 年以来,您输入的命令几乎没有变化。但其底层组件几乎都已替换:完整性检查、密钥交换、签名算法,以及代码库本身。只有在每次替换最终都主动移除旧组件的情况下,这才成为可能;而每次移除都会导致某些用户或系统出现问题。

因此,SSH 的安全性主要取决于版本。默认设置包含了以下决策:提供哪些算法、拒绝哪些算法,以及显示哪些警告。旧服务器会继续提供其版本仍允许的算法,并继续降级协商,以兼容旧客户端。截至 2026 年 8 月,当前版本为 OpenSSH 10.5,于 2026 年 8 月 11 日发布。当前版本与一台三年无人维护的机器上所运行版本之间的差距,正是问题的严重程度。检查版本应列入新 VPS 上前十分钟内的检查事项

FAQ

谁创建了 SSH,原因是什么?

Helsinki University of Technology 研究人员 Tatu Ylönen 于 1995 年创建了 SSH,此前该大学网络发生过密码嗅探攻击。当时的远程登录工具 telnet 和 rlogin 会以可读文本形式在网络上传输密码,因此,监控共享网段的任何人都能收集经过的凭据。他于 1995 年 7 月将该程序作为免费软件发布。到当年年底,该程序已在五十个国家拥有约 20,000 名用户;同年 12 月,他创办了 SSH Communications Security。

SSH-1 和 SSH-2 有什么区别?

它们是不同的协议,在线路级别不兼容。SSH-1 是一个单体协议,使用 CRC-32 确保完整性,并让客户端使用服务器的 RSA 密钥加密会话密钥后发送。SSH-2 将功能拆分为传输层、身份验证层和连接层(RFCs 4251 至 4254,2006 年 1 月),使用 HMAC 确保完整性,并通过 Diffie-Hellman 派生会话密钥。因此,即使之后主机密钥被盗,已记录的网络流量仍然保持私密。SSH-1 分阶段退出 OpenSSH,最终版本为 2017 年 10 月发布的 7.6。

为什么 OpenSSH 取代了原始 SSH 实现?

原始实现的开发后来转为商业产品,并采用限制性许可证;最后一个可自由复用的版本是 ssh 1.2.12。1999 年初,Björn Grönvall 将该版本重新开发为 OSSH,随后 OpenBSD 团队从 OSSH 分叉出 OpenSSH。OpenSSH 随 OpenBSD 2.6 于 1999 年 12 月 1 日发布。OpenBSD 需要经过审计且许可证不受限制的代码用于其基础系统,而这两个条件使其他操作系统都能通过 portable 分支发布同一实现。

为什么我第一次连接时,SSH 会询问主机密钥?

因为客户端此前从未见过该服务器,没有可用于比对其密钥的值。仅靠加密无法区分合法服务器和位于通信路径中间的机器,因此 SSH 使用密钥标识服务器,并将看到的内容记录在 ~/.ssh/known_hosts 中。首次连接时没有已存储的值可供检查,所以客户端会要求您确认。请将指纹与您从服务商控制台或服务器本身获取的指纹进行比对。之后如果出现 REMOTE HOST IDENTIFICATION HAS CHANGED 消息,在能够解释原因之前,应将其视为真实事件。

为什么升级后,较旧的 SSH 密钥会停止工作?

因为 OpenSSH 按照已公布的计划停用算法。DSA(ssh-dss)密钥在 2015 年 OpenSSH 7.0 中默认禁用,并于 2025 年 4 月 9 日在 OpenSSH 10.0 中彻底移除。RSA 密钥仍然可用,但使用 SHA-1 生成的签名在 2021 年 9 月的 OpenSSH 8.8 中默认禁用;连接旧服务器时,这会显示为 sign_and_send_pubkey: no mutual signature supported。Ed25519 密钥自 2014 年 1 月的 OpenSSH 6.5 起可用,可以避免这两个问题。