在任何VPS或云服务商上防止首次SSH连接的中间人攻击

Lobsters Hottest 工具

摘要

一种新技术,利用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 连接,攻击者还可能(额外)成功地攻击你的**工作站**。

相似文章

imthenachoman/如何保障 Linux 服务器安全

GitHub Trending (daily)

这是一份全面的开源指南与工具集,旨在保障 Linux 服务器的安全,涵盖使用 Ansible、Fail2Ban 和 Lynis 等工具进行 SSH 加固、防火墙配置以及入侵检测。

如果你喜欢MITM攻击,就别管DNSSEC

Lobsters Hottest

文章认为,忽略DNSSEC会让用户暴露在中间人攻击之下,并以电子邮件、Matrix和XMPP为例,将其与历史上对HTTPS的抵制进行类比。