SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-28

SSH的历史:从Telnet到OpenSSH

1995年赫尔辛基密码嗅探攻击催生SSH。本文核对从telnet、rlogin到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 本身都无法修复这一问题,因为这两个协议都没有提供承载修复方案的位置。

为何赫尔辛基的一次嗅探攻击催生了 SSH

1995 年,赫尔辛基理工大学的网络遭遇了一次 CERT 所描述类型的密码嗅探攻击。该校研究人员 Tatu Ylönen 编写了替代方案,并于 1995 年 7 月以免费软件形式发布。他将其命名为 Secure Shell。

这款软件依靠两个设计决策取得成功。会话经过加密,因此监听该网段的攻击者无法获取有用信息。服务器使用密钥证明自身身份,因此客户端可以判断自己是否连接到了正确的计算机;这正是 rlogin 的主机名信任机制留下的漏洞。

它还因为命令与用户原本输入的命令一致而迅速普及。ssh 取代了 rsh 和 rlogin,scp 取代了 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 等人几乎立即开始维护可移植分支,这就是版本号中的 p(例如 10.5p1)的来源。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 定义连接层。分层是其中的关键,因为这样可以分别替换各层。SSH-2 后续的大部分发展,都是在完成这种替换。

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

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

为何删除 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 可彻底阻止这一类攻击,因此它会出现在每份加固检查清单中。这样也会移除密钥失效时曾用于应急的备用登录方式。因此,在被锁在服务器外之前,请先学会区分所有会报告 Permission denied (publickey) 的不同故障。不再使用密码也意味着需要积累多个密钥。持有十几个密钥的 agent 会依次尝试每个密钥,直到服务器达到尝试次数上限并断开连接。这就是即使正确的密钥已加载,登录仍可能失败并显示 Too many authentication failures 的原因。密钥的生成和轮换见SSH 密钥管理基础,服务器端设置见加固 VPS 上的 SSH。

SSH 算法列表为何不断变化

分层协议允许算法逐步退出,而无需推出新的协议。OpenSSH 一直在利用这一特性,版本发布日期显示了这一变化的速度。

Ed25519 于 2014 年 1 月 30 日随 OpenSSH 6.5 发布,同时加入了 chacha20-poly1305 加密算法,以及由 bcrypt 保护的私钥格式。Ed25519 签名以确定性方式生成每个签名使用的 nonce,因此签名时随机数生成器较弱也不会泄露私钥。现实中的安全事件已经证明,DSA 和 ECDSA 私钥正是可以通过这种方式恢复的。

DSA 则走向了另一个方向。OpenSSH 7.0 在 2015 年禁用了 ssh-dss 主机密钥和用户密钥的运行时支持,因为该算法将私钥限制为 160 位,并且只能使用 SHA-1。版本 9.8 于 2024 年 7 月 1 日在编译时禁用了 DSA。版本 10.0 于 2025 年 4 月 9 日移除了 DSA。项目对此的表述是“完成始于 2015 年的弃用流程”。从禁用到删除,历时 10 年。

RSA 并未消失,但其旧签名格式已经移除。OpenSSH 8.8 于 2021 年 9 月 26 日起默认不再接受使用 SHA-1 生成的 RSA 签名。发行说明明确说明了原因:SHA-1 在密码学上已经不安全,攻击者可以以低于 50,000 美元的成本构造选择前缀碰撞。如果您在连接旧服务器时遇到过 sign_and_send_pubkey: no mutual signature supported,那就是这项变化导致的。您的密钥没有问题。对端要求使用的签名算法已经不安全。

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

** 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 上线后的前 10 分钟。

FAQ

SSH 由谁创建?为什么创建?

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

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

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

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

原始 SSH 的开发后来转为商业产品,并采用了限制性许可证;最后一个可自由复用的版本是 ssh 1.2.12。1999 年初,Björn Grönvall 以该版本为基础重新开发了 OSSH。随后,OpenBSD 团队从 OSSH 分支出 OpenSSH,并于 1999 年 12 月 1 日随 OpenBSD 2.6 发布。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 起可用,可以避免这两个问题。