在任何VPS或云服务商上防止首次SSH连接的中间人攻击
摘要
一种新技术,利用cloud-init注入临时SSH主机密钥,保护任何云服务商上新虚拟机的首次SSH连接免受中间人攻击。包含一个强化版开源脚本实现。
<p><a href="https://lobste.rs/s/q5bds7/stop_mitm_on_first_ssh_connection_on_any">评论</a></p>
查看缓存全文
缓存时间: 2026/05/08 12:28
# 在首次 SSH 连接时阻止中间人攻击,适用于任何 VPS 或云提供商 | 密码学、技术
来源:https://www.joachimschipper.nl/Stop%20MITM%20on%20the%20first%20SSH%20connection,%20on%20any%20VPS%20or%20cloud%20provider.html
这个轻量脚本(https://github.com/JoachimSchipper/ssh-init-vm/blob/main/ssh-init-vm)可以防止针对新虚拟机首次 SSH 连接的攻击,即使是在那些未提供专有解决方案(如 AWS 的 Systems Manager Session Manager(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-with-systems-manager-session-manager.html)、指纹验证(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connection-prereqs-general.html#connection-prereqs-fingerprint)或 flyctl 控制台(https://fly.io/docs/flyctl/console/))的提供商(如 Hetzner Cloud(https://www.hetzner.com/cloud))上;我们只需要 cloud-init(https://docs.cloud-init.io/),它被广泛支持(https://docs.cloud-init.io/en/latest/reference/availability.html#clouds)。
## 总结
(面向专家;如需更详细说明请继续阅读):通过 cloud-init(https://docs.cloud-init.io/en/latest/reference/modules.html#mod-cc-ssh:~:text=distros%20is%20dropped.-,Host%20keys,-Host%20keys%20are)注入一个临时 SSH 主机(私钥),然后仅在此临时密钥有效期内信任它,以便生成并获取“真正的”(长期)SSH 主机密钥。
该脚本(https://github.com/JoachimSchipper/ssh-init-vm/blob/main/ssh-init-vm)是这一技术的简洁但强化的实现;脚本中的注释讨论了实现选择。这项技术似乎是新的:我没有找到关于此技术的适当文章,也没有找到任何其他与提供商无关的解决方案(但如果有人能指正,我将欢迎指正(https://www.joachimschipper.nl/About%20me.html))。
这项技术实际上保护了首次连接,而仅仅在 `ssh` 询问“`The authenticity of host [...] can't be established`”时回答“yes”(即首次使用时信任),会让攻击者有机会将你的流量重定向到代理服务器,或者“慷慨地”决定为你提供虚拟机(……暂时如此)。这项技术还使得 cloud-init 用户数据的泄露变得无害。
通过 cloud-init(https://docs.cloud-init.io/en/latest/reference/modules.html#mod-cc-ssh:~:text=distros%20is%20dropped.-,Host%20keys,-Host%20keys%20are)注入一个**长期** SSH 主机(私钥)确实允许你对首次连接进行认证(通过将注入密钥的公钥部分添加到 `~/.ssh/known_hosts`),但这会将有价值的(私钥)材料留在 cloud-init 用户数据中,攻击者通常可以从以下途径获取:
- 元数据服务:虚拟机上的任何进程通常都能读取;例如在 Hetzner 虚拟机上(https://www.hetzner.com/cloud):
```
$ curl http://169.254.169.254/hetzner/v1/userdata
#cloud-config
ssh_keys:
ecdsa_private: |
-----BEGIN OPENSSH PRIVATE KEY-----
[...]
-----END OPENSSH PRIVATE KEY-----
ecdsa_public: ecdsa-sha2-nistp256 AAAAE2Vj[...]tI=
temporary host key for [...]
```
攻击者通常可以通过 SSRF(https://en.wikipedia.org/wiki/Server-side_request_forgery)欺骗某些进程泄露这些数据(即使提供商提供了解决方案,也常常未对 SSRF 做防护(https://aws.amazon.com/blogs/security/get-the-full-benefits-of-imdsv2-and-disable-imdsv1-across-your-aws-infrastructure/));或者从
- 提供商的其他系统(例如 Hetzner 明确警告不要存储“密码或其他敏感信息”(https://docs.hetzner.cloud/reference/cloud#tag/servers/create_server:~:text=don%E2%80%99t%20use%20it%20to%20store%20passwords%20or%20other%20sensitive%20information));或者从
- 你的管理工作站。
## 安全分析 / 威胁模型
整个过程中,我们信任 (Open)SSH 协议及实现,并且我们不依赖于你(管理员)检测到攻击(https://www.joachimschipper.nl/Stop%20MITM%20on%20the%20first%20SSH%20connection,%20on%20any%20VPS%20or%20cloud%20provider.html#double-authentication)。
### 我们能够抵抗网络攻击者
我们保护:
- 管理工作站的完整性,以及
- 虚拟机的安全,抵御一个
- 完全控制网络的攻击者(“中间人”),并且
- 在脚本终止(无论成功与否)之后任何时间点获知 cloud-init 用户数据的攻击者,
- 因为攻击者在密钥材料仍然有价值的时间点从未获知任何密钥材料。
为防止意外使用临时 SSH 主机密钥,脚本(https://github.com/JoachimSchipper/ssh-init-vm/blob/main/ssh-init-vm)将其存放在临时目录中;临时 SSH 主机密钥永远不会出现在 `~/.ssh/known_hosts` 中。
### 攻破管理工作站仍不能让攻击者获得(长期)SSH 主机(私钥)
我们保护:
- (仅)虚拟机,包括其(长期)SSH 主机(私钥),抵御一个
- 完全控制网络的攻击者(“中间人”),并且
- 完全控制管理工作站的攻击者,但
- 实际上并未连接到虚拟机(在真实场景中,这通常通过日志证明),
- 因为(长期)SSH 主机(私钥)从未出现在管理工作站上,且攻击者实际上并未连接到虚拟机。
(一个*确实*连接到虚拟机的攻击者很可能能够获知 SSH 主机密钥,例如通过 `ssh root@ cat /etc/ssh/ssh_host_*`。)
### 攻破虚拟机和/或提供商仍不能让攻击者获得管理员工作站
我们保护:
- (仅)管理工作站的完整性,抵御一个
- 完全控制网络的攻击者(“中间人”),并且
- 完全控制虚拟机和/或提供商的攻击者,
- 因为我们假定 (Open)SSH 是安全的。
作为此场景下的额外保障,脚本(https://github.com/JoachimSchipper/ssh-init-vm/blob/main/ssh-init-vm)并非简单地将虚拟机输出直接写入 `~/.ssh/known_hosts`,而是依赖 OpenSSH 的密钥轮换功能(https://man.openbsd.org/ssh_config#UpdateHostKeys)(https://blog.djm.net.au/2015/02/key-rotation-in-openssh-68.html)(https://blog.djm.net.au/2015/02/hostkey-rotation-redux.html)来放置长期 SSH 主机密钥,这样:
- 防止被攻陷的主机向 `ssh` 的 `known_hosts` 解析器注入恶意数据,
- 确保我们只将虚拟机**实际控制**的密钥(https://blog.djm.net.au/2015/02/hostkey-rotation-redux.html)写入 `~/.ssh/known_hosts`,
- 并确保我们正确处理 OpenSSH 选项,如 `HashKnownHosts`(https://man.openbsd.org/ssh_config#HashKnownHosts)(以及将来可能添加的任何相关选项)。
## 附注:……但是攻击者真的能让网络攻击奏效吗?
**视情况而定。** 特别是,如果你确实发现所有连接都始终指向错误的机器,并且你无法被说服输入密码(无论是首次还是后续连接),并且你没有配置 `ssh` 转发 agent 或 X11 连接,那么攻击者很可能失败。
以下是一个简化的非详尽列表,感谢 ssh-mitm(https://docs.ssh-mitm.at/):
- 如果攻击者能够通过提供一台攻击者控制的机器(而非*真正的*目标主机)来欺骗你,那么攻击者(很可能)成功;否则,
- 如果攻击者能够诱使你泄露能够登录真实主机的信息,则攻击者成功,即:
- 如果你使用密码登录(攻击者的机器);或者
- 如果你使用任何认证方法登录,然后在提示时输入密码;或者
- 如果你使用任何认证方法登录,并转发 ssh-agent 连接;
- 否则,攻击者很可能失败:他们需要访问真实主机才能欺骗你,但无法利用你的输入登录真实主机。
如果你使用任何认证方法并转发 X11 连接,攻击者还可能(额外)成功地攻击你的**工作站**。
相似文章
@dreamsofcode_io: 现在正是考虑将你的 SSH 密钥放在硬件安全密钥(如 Yubikey)上的好时机。
一条推文建议将 SSH 密钥使用硬件安全密钥(如 Yubikey)进行保护,并提及 npm、PyPI 和 Crates.io 上正在活跃的跨生态系统供应链攻击(TrapDoor),该攻击涉及恶意包和窃取加密货币的恶意软件。
imthenachoman/如何保障 Linux 服务器安全
这是一份全面的开源指南与工具集,旨在保障 Linux 服务器的安全,涵盖使用 Ansible、Fail2Ban 和 Lynis 等工具进行 SSH 加固、防火墙配置以及入侵检测。
如果你喜欢MITM攻击,就别管DNSSEC
文章认为,忽略DNSSEC会让用户暴露在中间人攻击之下,并以电子邮件、Matrix和XMPP为例,将其与历史上对HTTPS的抵制进行类比。
Show HN: Mezz, 一个可通过curl使用的WiFi沙箱,用于物联网渗透测试
Mezz 是一个自包含的WiFi沙箱工具,用于检查物联网设备流量,提供一个隔离网络,带有DNS日志记录和可选的MITM代理,全部可通过Docker部署在具有AP功能的WiFi的Linux主机上。
解决“有用但不安全”的困境:非隔离代理的一次性管理员审批
本文介绍了 prompt2bot 中针对非隔离 AI 代理的一次性管理员审批机制,通过要求管理员确认执行敏感工具(如创建虚拟机或执行代码)来防止 prompt 注入攻击。