SSH 协议深度解析:从握手到加密隧道

SSH 这东西,说白了就是远程管理的「安全通道」。我早年做运维时,公司还在用 Telnet,密码明文传输,抓个包就能看到 root 密码。后来换了 SSH,才算是睡了个安稳觉。但你真的了解 SSH 内部是怎么工作的吗?

这一节,我们把它拆开来看。

SSH 的三层架构

SSH 协议分为三层,从上到下依次是:

  • 传输层协议:负责服务器认证、密钥交换、数据加密。这是安全的基础。
  • 用户认证协议:验证客户端用户的身份。密码、公钥、键盘交互都在这一层。
  • 连接协议:将加密通道复用为多个逻辑通道。比如你同时跑 SCP、端口转发、远程命令,都是靠它。

这三层是独立的,但协同工作。我见过有人只改 SSH 端口就觉得安全了,其实连第一层都没搞明白。

密钥交换机制:如何安全地建立信任

SSH 的密钥交换,核心目标是:在不安全的网络上,安全地协商出一个对称加密密钥。

流程大致如下:

  1. 客户端发起连接,服务器发送自己的主机密钥公钥。
  2. 双方协商加密算法、MAC 算法、压缩算法等。
  3. 使用 Diffie-Hellman 算法(或椭圆曲线变体)生成共享密钥。
  4. 服务器用主机密钥对交换过程中的关键信息签名,客户端验证签名。
  5. 双方用共享密钥派生会话密钥,后续通信全部加密。

嗯,这里要注意:第一次连接时,客户端会收到服务器的主机密钥指纹。你确认过这个指纹吗?我见过太多人直接输入 yes 跳过验证,结果中间人攻击就进来了。

核心原则:永远不要跳过主机密钥验证。把服务器指纹记录下来,每次连接时比对。

加密算法选择:别用默认配置

SSH 支持多种加密算法,但默认配置往往不是最安全的。我个人习惯,会主动禁用一些老旧算法。

下面这张表,是我常用的算法推荐:

算法类型 推荐算法 应禁用算法 说明
密钥交换 curve25519-sha256, diffie-hellman-group-exchange-sha256 diffie-hellman-group14-sha1, diffie-hellman-group1-sha1 椭圆曲线更快更安全,SHA1 已不推荐
对称加密 chacha20-poly1305, aes256-gcm aes128-cbc, 3des-cbc, blowfish-cbc GCM 模式自带认证,CBC 模式有 padding oracle 风险
MAC 算法 hmac-sha2-256, hmac-sha2-512 hmac-sha1, hmac-md5 SHA1 碰撞攻击已成现实
主机密钥 ed25519, rsa-sha2-512 ssh-rsa (SHA1 签名), dsa ed25519 性能好,密钥短,推荐优先使用

我的经验:在 /etc/ssh/sshd_config 中,用 Ciphers、MACs、KexAlgorithms、HostKeyAlgorithms 四个指令来锁定算法。我曾经帮一家金融客户审计,发现他们还在用 3des-cbc,那台服务器跑了 5 年没重启过……

常见配置优化:让 SSH 既安全又好用

配置优化不是越严越好,还要考虑运维效率。下面是我常用的几项:

  • 禁用 root 直接登录:PermitRootLogin no。用普通用户登录,再 su 或 sudo。这是最基本的安全策略。
  • 使用密钥认证,禁用密码:PasswordAuthentication no。密码容易被暴力破解,密钥要安全得多。
  • 限制登录用户/组:AllowUsers user1 user2 或 AllowGroups sshusers。减少攻击面。
  • 修改监听端口:Port 2222。虽然不能真正防住扫描,但能减少日志噪音。
  • 启用公钥指纹验证:在客户端配置 StrictHostKeyChecking ask,不要设为 no。
  • 设置空闲超时:ClientAliveInterval 300 和 ClientAliveCountMax 2。防止僵尸连接。

警告:修改 SSH 配置前,一定要先开一个备用连接。我曾经在远程服务器上改了配置,结果把自己锁在外面,最后只能让机房同事帮忙重启……嗯,从那以后我改配置前都会先开两个会话。

SSH 密钥交换与加密流程全景图

下面这张图,展示了 SSH 从建立连接到加密通信的完整流程:

SSH 密钥交换与加密流程 SSH 密钥交换与加密通信流程 客户端 服务端 1. 发起 TCP 连接 接受连接 2. 发送算法列表 选择算法并回复 3. DH 公钥 + 签名 DH 公钥 + 签名 4. 计算共享密钥 计算共享密钥 5. 加密通信开始 加密通信开始 加密隧道(AES-256-GCM / ChaCha20-Poly1305) 主机密钥用于验证服务器身份,防止中间人攻击 共享密钥由 DH 算法协商,每次会话独立

实战:SSH 配置文件示例

下面是我个人常用的 sshd_config 安全配置片段。你可以直接参考:

# /etc/ssh/sshd_config 安全加固片段

# 端口和监听
Port 2222
ListenAddress 0.0.0.0

# 认证方式
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey

# 密钥算法
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

# 加密算法
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512,hmac-sha2-256
KexAlgorithms curve25519-sha256,diffie-hellman-group-exchange-sha256

# 用户限制
AllowUsers admin devops
AllowGroups sshusers

# 连接控制
ClientAliveInterval 300
ClientAliveCountMax 2
MaxAuthTries 3
MaxSessions 10

# 日志
LogLevel VERBOSE

小技巧:配置改完后,先执行 sshd -t 检查语法,再重启服务。我习惯用 systemctl reload sshd 而不是 restart,这样不会中断现有连接。

避坑指南:我踩过的 SSH 坑

做安全这么多年,SSH 相关的坑没少踩。分享几个典型的:

  • 密钥权限问题:私钥文件权限必须是 600,公钥可以是 644。我曾经把私钥设成 644,SSH 直接拒绝使用,排查了半天。
  • authorized_keys 文件权限:这个文件以及父目录 ~/.ssh 的权限都不能太宽松。~/.ssh 应该是 700,authorized_keys 是 600。否则 SSH 会忽略这个文件。
  • 算法降级攻击:如果服务器同时支持弱算法,攻击者可以强制协商到弱算法。所以一定要显式禁用不安全的算法。
  • 主机密钥变更:服务器重装后主机密钥会变,客户端会报错。这时候要确认不是中间人攻击,再手动清除旧的 known_hosts 记录。

嗯,SSH 这东西,配置好了可以很安全,配置不好就是个筛子。希望这一节能帮你把 SSH 的底裤看透。