SSH(Secure Shell)是一种用于与远程计算机进行加密通信和执行命令的协议。这种连接需要双向验证。客户端验证所连接的服务器是否是预期的服务器,服务器则验证连接用户是否有登录权限。用户公钥认证成功并不意味着首次遇到的服务器身份也得到了验证。
继通过SSH访问Docker Socket的文章和Mac远程工具比较之后,本文重点关注信任服务器主机密钥的过程。特别是区分了通过ssh-keyscan收集的密钥和独立验证的密钥。
所有示例都使用假想的lab-vm.example和图示用的密钥A/B。A/B并非实际的SSH密钥或指纹。我没有连接到实际服务器,也没有生成认证密钥,更没有执行下面的SSH命令。官方文档于2026年10月15日确认。

AI生成的概念图。并非实际的SSH屏幕、密钥、指纹或测试结果。
1. 服务器密钥和用户密钥回答不同的问题
服务器主机密钥用于验证服务器的身份。客户端检查预期主机的公钥与连接方提供的密钥之间的关系。在用户公钥认证中,服务器验证用户是否可以使用相应的私钥进行认证,以及该公钥是否被该账户授权。这并非将私钥复制到服务器的过程。OpenSSH认证和主机密钥验证
클라이언트 ── 서버 호스트 키 확인 ──→ 기대한 lab-vm.example인가?
클라이언트 ── 사용자 공개키 인증 ──→ 이 사용자의 로그인을 서버가 허용하는가?
将其按文件角色划分会更清晰。
| 项目 | 主要由谁验证 | 包含内容 | 回答的问题 |
|---|---|---|---|
客户端的known_hosts |
SSH客户端 | 记住或预先注册的服务器主机公钥 | 本次服务器密钥是否与预期记录匹配? |
服务器账户的authorized_keys |
SSH服务器 | 该账户允许的用户公钥 | 此用户密钥是否可以登录到相应账户? |
除了常见的按用户文件外,还可能存在系统级的主机密钥列表或其他认证配置。本文以不含证书·主机CA的简单主机公钥记录和用户公钥认证为基准。这并非说明所有SSH发行版都仅通过这两个文件运行。
例如,即使用户的公钥已正确注册到authorized_keys中,如果客户端首次连接到错误的服务器并信任了其密钥,那么服务器身份验证的前提就是错误的。反之,即使服务器密钥匹配,如果用户公钥未被授权,登录也可能失败。单独记录这两个成功条件有助于区分原因。

图:主机密钥和用户公钥的作用。基于官方文档重构的概念图,不代表实际密钥或连接测试。官方依据1 · 官方依据2
2. 存储首次见到的密钥与验证其身份不同
假设首次连接的lab-vm.example提供了密钥A。如果客户端尚未有记录,则没有可供比较的标准。此时屏幕上显示的指纹是当前响应方的公钥摘要值。仅仅显示这一点并不能证明对方是管理员准备的服务器。
指纹是为方便比对长公钥而创建的识别表示。如果您已经知道预期的指纹,可以将其与显示的值进行比较。OpenSSH手册也描述了获取服务器主机公钥指纹并验证其是否与已知指纹匹配的步骤。主机密钥指纹验证
这里获取基准值的路径至关重要。作者的建议是,除了要验证的新SSH连接之外,应使用可信的管理控制台、已确认的运维人员渠道或预先批准的部署记录。如果从相同的未验证连接路径同时获取密钥和“正确指纹”,则无法进行独立比对。
如果密钥A与可信基准A一致,则可以将该记录与相应目标关联并进行管理。如果缺少基准或A/B不同,则首先检查目标和更改内容。没有必要将图示中的A/B装饰成实际指纹的形状进行公开。
3. ssh-keyscan是收集工具,而非信任的起点
ssh-keyscan是收集服务器提供的主机公钥的工具。官方手册警告说,如果收集到的密钥未经验证就用于创建主机列表,则会存在中间人攻击的风险。ssh-keyscan官方文档
以下是创建假想候选密钥文件的未执行示例。这并非涉及实际服务器或密钥文件的实践。
# 未执行:将响应方的密钥收集到单独的候选文件中的形式
ssh-keyscan -t ed25519 lab-vm.example > candidate-hostkeys.txt
# 未执行:显示已收集的候选文件指纹的形式
ssh-keygen -l -f candidate-hostkeys.txt
第二个命令仅将文件中的密钥显示为指纹,并不认证文件的来源。ssh-keygen的-l是显示密钥指纹的选项,在此示例中并非用于生成新认证密钥的命令。ssh-keygen选项
即使在使用将收集结果添加到主机记录的操作时,如现有/589的CI连接示例,服务器身份验证仍需要单独的验证标准。本续篇补充的部分是收集 → 与信任路径的基准进行比对 → 注册已批准的记录。因注册而消除连接错误的事实与密钥真伪验证是不同的结果。
如果未经验证的候选B立即被保存为“预期密钥B”,那么后续连接可能会持续匹配B。记录稳定保持的事实并不能证明最初信任B的判断是正确的。
将要注册的基准与连接中观察到的值分开
公钥并非秘密值,因此仅仅看到密钥字符串并不能证明服务器拥有该密钥的私钥。实际的SSH连接同时处理服务器提供的主机密钥和协议的密码学证明。ssh-keyscan候选文件并非替代连接验证的登录成功证据。OpenSSH主机密钥验证
简单的审查记录如果分为三列也会有所帮助:要连接的目标和端口、从可信路径获得的预期密钥、实际连接提供的密钥。图示中A和B匹配的模型仅表达了这种比较的逻辑。不应将其解读为已测试实际签名验证或DNS·中转服务器配置。
4. 理解accept-new所做工作的范围
StrictHostKeyChecking决定如何处理未知或已更改的主机密钥。下表描述了本文中使用的简单密钥记录情况。额外的信任配置和自动密钥更新功能是独立的。
从图示来看,两种情况是不同的。
기록 없음 + 제시 B + accept-new → B를 최초 기록으로 삼음
기록 A + 제시 B + accept-new → 변경으로 거절
在自动化中,可以考虑预先提供经过验证的主机密钥列表。还需要管理密钥所属的名称、端口和连接路径。本文不建议将StrictHostKeyChecking=no、忽略主机记录或删除整个文件作为解决连接失败的通用方法,因为这样做会消除要验证的基准本身。
5. 密钥更改警告应首先确认目标和更改原因
如果昨天记录的A与今天提供的B不同,原因并非单一。计划的服务器重装·密钥更换、目标地址或连接路径的变化、意外的对方等都是需要验证的假设。仅凭警告无法确定是否遭到入侵,也无法断定是正常更改。
作者的检查顺序如下:
- 停止当前连接并确认目标。检查输入的名称、端口以及别名·中转配置是否是预期的目标。
- 保留现有记录A和更改警告。不要为了消除警告而删除整个
known_hosts。不要将实际警告或识别值复制到公开文章中。 - 通过可信路径确认更改原因和新基准。确认是否是计划的更换,是否是正确的目标,以及密钥类型和指纹比对基准是否正确。
- 仅更新已验证的范围。当获得将B视为预期新密钥的依据时,仅通过批准的程序更改该目标的记录。如果依据不足,则暂缓。
- 重新连接时,以相同的基准进行确认。分别查看服务器密钥验证结果和用户登录结果。
还有按名称或端口查找已存储记录的选项。以下同样是语法说明用的未执行示例,此操作并未实际读取known_hosts。
# 未执行:查找特定目标记录的形式
ssh-keygen -F lab-vm.example
ssh-keygen -F '[lab-vm.example]:2222'
在完全删除后,B被接受为“新密钥”并非验证了警告所指问题的结果。这只是通过消除比较记录,回到了最初的信任问题。安全的更新不是首先选择删除命令,而是首先确保更改密钥的依据。
6. 使用合成策略模型而非实际连接来验证边界
附件中的verify_ssh_trust_model.py是一个由字符串A/B构建的策略模型,而非实际的OpenSSH。它不涉及外部连接、密钥生成、指纹计算或读取实际认证文件。
python3 verify_ssh_trust_model.py --output results.json
在Python 3.9.6中,8项合成逻辑检查全部通过。它检查了拒绝未知密钥、首次密钥自动记录的未验证状态、拒绝A→B更改、服务器密钥匹配与用户权限的独立性、收集值与信任基准不一致、注册已验证基准和删除现有记录的影响。还包括了按名称·端口分开管理的虚拟案例。
此PASS表示模型中编写的判断规则在示例中是一致的。它并非验证SSH握手、密码学签名、实际指纹或用户环境中的配置操作的结果。读者应根据自己使用的OpenSSH版本和信任配置进行单独验证。
判断登录成功时,保持两个问题会有帮助:是否有证据表明它是预期服务器的密钥,以及该服务器是否已授权用户登录?加密连接和用户公钥认证,再加上服务器身份验证,才能解释您正在与哪个对方进行通信。
发表回复