生产环境SSH弱加密算法排查与安全加固实战指南

生产环境SSH弱加密算法排查与安全加固实战指南 第一次在线上环境里看到安全扫描报告把 SSH 服务标成高危时我还是有点懵的。明明密码策略、登录失败锁定、端口白名单都做了怎么问题还出在最基础的加密算法上。后来才意识到SSH 服务能不能扛住攻击不只是看密码强不强、密钥管得好不好更要看它愿意跟客户端协商哪些算法。如果服务端还在默许 3des-cbc、RC4、hmac-sha1 这种老古董等于主动把门锁降级成了插销。这篇就把我在生产环境里修复 SSH 弱加密算法的完整过程写出来从原理、排查、方案设计到最终落地给同样踩坑的运维同行一个可以直接抄作业的参考。1. 为什么要关注 SSH 弱加密算法1.1 SSH 服务原理一次连接到底协商了什么很多人对 SSH 的理解停留在用 22 端口加密登录远程服务器这个层面但实际登录背后发生的事比想象中复杂。SSH 协议不是一上来就传账号密码而是要经过 TCP 连接、版本交换、算法协商、密钥交换、主机密钥验证、会话加密等多个阶段。算法协商这一步是安全性的分水岭。客户端和服务端各自维护一份支持的算法列表包括密钥交换算法KexAlgorithms、主机密钥算法HostKeyAlgorithms、对称加密算法Ciphers、消息认证码算法MACs等。连接建立时双方会互相发送自己支持的算法清单然后按照取交集、选优先级最高的规则确定本次会话实际使用的算法组合。问题恰恰出在这个取交集的机制上。服务端如果把弱算法也放在列表里客户端只要愿意用协商结果就会降级到最薄弱的一环。比如服务端同时支持 aes256-gcm 和 aes128-cbc老客户端又恰好优先请求 aes128-cbc那本次会话的数据加密就用了已经被学术界判定为不安全的 CBC 模式。运维人员以为 SSH 加密很可靠实际上流量早就被套上了老式算法。1.2 弱加密算法常见的攻击路径从实际攻击场景来看弱加密算法主要暴露三类风险。第一类是算法降级攻击。攻击者不需要直接破解强算法而是通过伪造或篡改算法协商过程让客户端和服务端落到弱算法上。只要服务端配置里还留着弱算法攻击者就有了可乘之机。这类攻击对用户是透明的登录界面没有任何异常提示。第二类是已知算法漏洞利用。RC4arcfour加密算法早已被证明存在明显偏差可以在足够多的密文中恢复明文3des-cbc 的 64 位分组长度在面对现代密码分析时也不再安全hmac-sha1 和 hmac-md5 作为消息认证码碰撞攻击成本逐年下降。这些算法一旦被协商成功等于给攻击者递了一根撬棍。第三类是暴力破解与横向渗透的组合利用。弱算法本身不一定直接被攻破但会明显降低攻击者的破解成本并让嗅探到的会话数据更容易被离线分析。尤其在混合云、多机房互通的环境下SSH 流量一旦经过不可控链路弱算法带来的风险就会被无限放大。1.3 安全扫描为什么总把这些算法标成高危我一开始也疑惑为什么扫描器动不动就把 SSH 降级、弱加密算法标成 High 甚至 Critical。这些扫描器通常采用 IETF 维护的算法推荐标准和各大厂商的最佳实践。只要服务端支持任何已被列入不应使用清单的算法扫描结果就会告警不会去判断该算法实际是否被使用。打个比方你家里的防盗门装了一把 C 级锁芯但门框上还并排挂着一把老式挂锁。小偷不一定每次都会去开挂锁但只要挂锁还在风险评估就认为存在被打开的可能性。安全扫描的思路也一样只要弱算法还在服务端列表里就算平时协商到的都是强算法风险等级依然居高不下。所以修复弱加密算法的本质就是做减法把协议协商阶段的可选范围缩到安全基线之内让系统根本没有机会走到弱算法那条路径上。2. 动手前先做算法清单体检2.1 用 ssh -Q 获取版本支持范围修复之前必须搞清楚两件事当前 OpenSSH 服务端支持哪些算法以及当前配置实际启用了哪些算法。前者是版本的固有能力后者才是需要关注的对象。OpenSSH 7.6 及以上版本提供了一组很实用的查询命令。在服务器上执行ssh -Q cipher可以列出所有支持的对称加密算法ssh -Q mac列出消息认证码算法ssh -Q kex列密钥交换算法ssh -Q key列主机密钥类型。这组命令的输出结果跟具体配置文件无关是二进制文件编译时带进去的能力边界。真正的生效配置需要通过sshd -T查看。这条命令会读取当前配置文件把最终生效的参数打印出来比直接看 sshd_config 更靠谱。因为 sshd_config 里支持 include 指令、条件匹配块肉眼容易漏掉某些优先级关系。执行sshd -T | grep -E ciphers|macs|kexalgorithms就能拿到当前服务端实际对外公布的算法列表。注意sshd -T 需要 root 权限执行某些发行版在非 root 用户下会提示需要权限或无法完成全部配置解析。2.2 用 ssh-audit 扫描线上服务光看服务端自己输出的列表还不够因为配置最终要通过网络对外体现。推荐在任意一台能访问目标服务器的机器上用 ssh-audit 做一次外部视角的扫描。这个工具是一个 Python 脚本不需要安装到目标服务器上执行起来很方便。git clone https://github.com/jtesta/ssh-audit cd ssh-audit python3 ssh-audit.py -p 22 192.168.1.100扫描结果会按颜色分级红色标记的是不推荐算法黄色是存在隐患的算法绿色是安全的算法。输出里还会附带改进建议比如remove cipher aes128-cbc或者use hostkey algorithm ssh-ed25519。这个工具检查得非常细甚至能给出本版本 OpenSSH 是否已经连接到密码学不安全算法端口上的建议。实测下来ssh-audit 的输出比手动看配置更直观而且能发现一些隐藏问题。比如曾经有一次扫描结果显示主机密钥算法里有 ssh-rsa这里指 SHA-1 签名的 RSA 密钥但 sshd_config 里完全没有配置 HostKeyAlgorithms。原因是当前 SSH 版本默认启用了这个算法没写配置不等于没有隐患。2.3 读懂扫出来的算法列表拿到扫描结果后重点看三类算法。加密算法里凡是带 cbc 的例如 aes128-cbc、aes256-cbc、3des-cbc基本可以直接划进禁用名单。blowfish-cbc、cast128-cbc 这类老牌算法也已经没有启用价值。rc4、arcfour 但凡存在都应该立即处理。当前推荐保留的是 CTR 模式的 aes 系列和 AEAD 类算法比如 aes128-ctr、aes192-ctr、aes256-ctr、aes128-gcmopenssh.com、aes256-gcmopenssh.com、chacha20-poly1305openssh.com。消息认证码算法里hmac-md5、hmac-md5-96、hmac-sha1、hmac-sha1-96、hmac-ripemd160 这类都建议禁用。推荐保留的是 sha2 系列比如 hmac-sha2-256、hmac-sha2-512以及支持 Encrypt-then-MAC 模式的变体。密钥交换算法里diffie-hellman-group1-sha1 属于必须删除的group14-sha1 也建议升级到 group14-sha256 或 group16-sha512。当前主流是 curve25519-sha256、curve25519-sha256libssh.org以及 ecdh-sha2-nistp256/384/521。需要说明的是nistp 系列的椭圆曲线虽然在部分安全社区有争议但在正式推荐的 OpenSSH 默认算法里依然在列实际使用问题不大。3. 修复方案设计不是删得越狠越好3.1 服务端算法白名单怎么定设计算法白名单时第一反应往往是只留最安全的几种算法。但在生产环境里这种思路容易把自己玩死。因为线上不只有最新版 OpenSSH 对连你还有各种运维脚本、监控系统、备份工具、网络设备它们内置的 SSH 客户端版本参差不齐。我的建议是分三步做第一步清理掉明确不安全的算法不带任何犹豫。CBC 系加密、RC4、hmac-md5、group1-sha1、hmac-sha1 等全部移除。第二步保留当前版本 OpenSSH 默认支持且安全性没有争议的算法作为主体。以 RHEL/CentOS 8 自带的 OpenSSH 8.0 为例最后我保留的加密算法是 aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcmopenssh.com, aes256-gcmopenssh.com, chacha20-poly1305openssh.com。MAC 算法保留 hmac-sha2-256, hmac-sha2-512, umac-128-etmopenssh.com, hmac-sha2-256-etmopenssh.com, hmac-sha2-512-etmopenssh.com。密钥交换算法保留 curve25519-sha256, curve25519-sha256libssh.org, diffie-hellman-group14-sha256, diffie-hellman-group16-sha512。第三步确认主机密钥算法。OpenSSH 8.0 默认支持 ssh-ed25519、ecdsa-sha2-nistp256/384/521、rsa-sha2-256、rsa-sha2-512以及已标记为废弃的 ssh-rsa。如果直接写死 HostKeyAlgorithms务必把 rsa-sha2 系带上同时移除 ssh-rsa。要是不写这一项默认行为里 ssh-rsa 可能还在扫描器照样会标记。3.2 老客户端和兼容性如何处理设计白名单的时候如果测试发现某些老设备连不上别急着把弱算法加回去。先按老设备到底缺哪个算法的思路排查再针对性处理。比如某些网络设备自带的 SSH 客户端只支持 diffie-hellman-group1-sha1 或 diffie-hellman-group14-sha1对应服务端就需要保留 group14-sha1。这种情况下可以评估两台设备之间的链路是否可信、是否在隔离网段。如果链路是可控内网可以单独为这几台设备所在的网段做条件配置设置宽松一点的算法集而不是在全局配置里无差别放开。OpenSSH 的 Match 条件块可以解决这个诉求。下面是一个简化的例子Match Address 10.20.30.0/24 KexAlgorithms curve25519-sha256,diffie-hellman-group14-sha1这样一来普通用户和业务系统走全局强算法指定老网段单独兼容。等老设备完成升级后再把这个条件块删掉即可。这种局部放行的方式在安全审计时也说得清楚风险范围是明确可控的。3.3 证书主机密钥的同步升级部分环境里 SSH 用的不是普通公钥认证而是基于 CA 签发的证书认证。这种情况下不光要改算法列表还要检查 CA 本身的算法强度。如果 CA 密钥仍然是 RSA 1024 或基于 SHA-1 签名即使把 sshd_config 改得再严格信任链源头依然薄弱。建议确认 CA 密钥对采用 RSA 3072/4096 或 Ed25519签名算法不涉及 SHA-1。如果证书签发体系在别的团队手里至少要在本服务端配置里把TrustedUserCAKeys指向的 CA 公钥文件更新到强算法版本并重新分发生成的用户证书。这块内容很多人容易忽略但安全扫描的视野往往是从握手算法一路看到认证链的任何一个环节出现 SHA-1都会被单独拎出来告警。4. 生产环境完整修复流程4.1 修改前准备与备份正式动手前先做三件事。第一件事备份配置文件。这个操作简单但不能省。执行cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date %F_%H%M%S)出现任何问题都可以快速回滚。第二件事确认当前所有 SSH 会话清单。执行who或ss -tnp | grep :22确认当前有哪些活跃会话。修改配置前最好确保有至少两个稳定的会话窗口或者使用 nohup 方式保留一个后台任务做兜底。第三件事检查是否有针对 sshd 的自动化任务。比如配置管理工具 Ansible、SaltStack 会在同步时重写 sshd_config监控系统会定时探测 22 端口。要确认修改不会被自动化任务覆盖否则排查问题时容易出现改了等于没改的处境。4.2 修改 sshd_config 核心配置用编辑器打开/etc/ssh/sshd_config在文件末尾追加或替换以下几行配置。建议把算法配置集中放在一个区域里加上注释方便后来的人快速定位。# Security hardening: disallow weak algorithms Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcmopenssh.com,aes256-gcmopenssh.com,chacha20-poly1305openssh.com MACs hmac-sha2-256,hmac-sha2-512,umac-128-etmopenssh.com,hmac-sha2-256-etmopenssh.com,hmac-sha2-512-etmopenssh.com KexAlgorithms curve25519-sha256,curve25519-sha256libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512 HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-256,rsa-sha2-512这里有几个细节需要说明。Ciphers 的顺序会影响协商优先级。OpenSSH 在服务端配置里给出的算法顺序即优先级顺序所以把性能和安全兼顾的 chacha20-poly1305 放在后面把兼容性最好的 aes128-ctr 放在前面实际使用中可以让不同客户端各自选中自己最合适的算法又不会掉到不安全区间。MACs 里建议优先保留 EtMencrypt-then-mac变体。这类算法先加密后计算 MAC比传统的先 MAC 后加密设计更安全能避免针对 CBC 模式的 oracle 攻击变形。KexAlgorithms 里 curve25519-sha256 是当前默认最优选择。group14-sha256 和 group16-sha512 作为兼容性选项保留能满足绝大多数现代化客户端的协商需求。HostKeyAlgorithms 要特别注意不要漏掉 rsa-sha2-256 和 rsa-sha2-512。如果你服务器上主要用的还是 RSA 主机密钥漏掉这两个会让所有基于 RSA 主机密钥的客户端连接失败。而 ssh-rsa 这个老算法一定要移除它是知名扫描器重点关注的对象。4.3 语法校验与平滑生效修改配置文件后不要直接重启服务。先执行sshd -t做语法检查。这条命令只校验配置格式不会影响当前运行中的服务。输出没有任何提示就代表配置语法没问题。接着执行systemctl reload sshd让配置生效。reload 和 restart 不同reload 只是在运行中的 sshd 主进程上重新读取配置不会断开已经建立的 SSH 连接。对于依赖长连接传输大文件或跑长时间任务的场景这个差异非常重要。这里还要提一个容易踩的坑某些发行版上/etc/ssh/sshd_config文件默认含有Include /etc/ssh/sshd_config.d/*.conf指令后加载的 .conf 文件会覆盖主文件里的同名配置。如果系统里存在/etc/ssh/sshd_config.d/下有其他配置文件光改主文件可能不生效。修改后一定要用sshd -T | grep -E ciphers|macs|kexalgorithms确认最终生效值不要只看文件内容。4.4 新窗口验证与回滚预案配置生效后千万不要关闭当前连接。重新开一个终端窗口尝试 SSH 登录同一台服务器。验证内容包括ssh -vvv userserver加上-vvv参数可以看到详细的算法协商过程。输出里会有kex: algorithm: curve25519-sha256、kex: host key algorithm: ssh-ed25519、cipher: aes256-gcmopenssh.com、mac: hmac-sha2-256之类的信息。逐项核对是否都在白名单内。另外建议顺手用nmap --script ssh2-enum-algos -p 22 server_ip再从外部扫一次确认服务端实际支持的算法列表已经更新。这一步能避免客户端因为本地缓存或代理导致的验证偏差。如果新窗口连接失败或者协商出来的算法不符合预期马上回滚。回滚操作很简单用之前备份的配置文件覆盖回去再执行sshd -t systemctl reload sshd。整个过程不会影响已建立的会话因为 reload 不会断连。5. 常见问题与排查技巧实录5.1 连接失败no matching cipher / kex最常见的连接报错是Unable to negotiate with x.x.x.x port 22: no matching key exchange method found。这个报错直译是找不到匹配的密钥交换算法。原因通常是客户端支持的算法和服务端白名单没有交集。排查思路很直接看客户端支持哪些算法再看服务端启用哪些算法。客户端侧用ssh -Q kex查支持列表服务端用sshd -T | grep kexalgorithms查生效列表。两个列表取交集如果为空说明真的没有共同语言。这类问题往往出现在老设备上。比如某台旧交换机只支持 diffie-hellman-group1-sha1而服务端白名单里没有这个算法。解决办法是评估是否需要单独为这个设备所在的网段开条件配置而不是全局加回 group1-sha1。因为把 group1-sha1 加到全局等于让所有客户端都有机会降级到这个算法违背了修复初衷。5.2 配置不生效优先级问题有一种情况很迷惑配置文件明明改了sshd -T 输出的算法列表却还是老样子。这大概率是 Include 指令的优先级问题。OpenSSH 解析配置的原则是后出现的配置项覆盖前面出现的配置项。主配置文件中如果有Include /etc/ssh/sshd_config.d/*.conf并且 include 指令写在文件末尾那么 .conf 目录下的任何同名配置项都会覆盖主文件内容。排查时先执行sshd -T | grep -i include或者直接查看主文件开头部分是否引用了额外目录。如果确认存在 .conf 覆盖要么把算法配置统一写到 .conf 文件里要么临时把 .conf 里的相关配置项注释掉。最稳妥的做法是统一管理只保留一个配置入口避免多处维护形成漂移。另外有些自动化脚本会定期重写配置文件。如果配置总是被重置检查 cron、Ansible、SaltStack 或系统加固脚本是否存在相关逻辑。我之前就碰到过加固脚本每周日凌晨自动把 Ciphers 重置成默认值的情况排查了很久才发现是 cron 任务在作怪。5.3 自动化任务和旧脚本的兼容性线上环境经常会有一批基于老参数写的自动化脚本。比如某些发布系统会直接执行scp -c aes128-cbc或者用带-o Ciphersarcfour的 ssh 命令传输数据。这种脚本在弱算法禁用后会立刻报错。应对办法是逐个梳理这些脚本能改的直接把算法参数改成现代算法不能改的评估是否需要临时白名单。但这里有个原则能用升级脚本解决的不要在服务端放行弱算法。放行了一个 arcfour可能就是为了一个两年前就该废弃的脚本风险完全不匹配。排查自动化脚本时可以通过登录日志辅助判断。所有因为算法协商失败而产生的连接尝试都会记录在/var/log/secure或/var/log/auth.log里看到对应报错后顺藤摸瓜找到调用方。5.4 别把自己锁在门外的操作纪律生产环境调整 SSH 配置最担心的就是把自己锁在门外。除了前面说过的新窗口验证法还有几条操作纪律值得分享。第一不要在业务高峰期做这类变更。SSH 配置重载理论上影响面可控但万一出现兼容性问题恢复过程会变成一场灾难。第二始终保持有一个可用窗口。哪怕只是开个交互式 shell 挂着也比全部依赖新连接验证要安全。因为 reload 不会断已有连接只要有一个可用窗口即使新连接全部失败也能从容回滚。第三准备好带外管理通道。比如云控制台的 VNC/管理终端或者物理服务器上的 BMC/IPMI。这个通道跟 SSH 是独立的能在 SSH 完全不可用的情况下进行救援操作。有了这层保障再改配置心里就不慌了。6. 修复后的常态化审计6.1 把算法审计纳入巡检脚本弱算法修复不是一次性的工作。新版本 OpenSSH 会不断调整默认算法列表安全基线也在持续变化。今天安全的算法可能过几年就不再推荐。建议把 SSH 算法审计纳入定期巡检脚本。一个简单的巡检逻辑是每天用 ssh-audit 扫描一次关键服务器把扫描结果中标记为 fail 的项输出到日志配合通知机制。这样任何一台服务器出现配置回退或者新引入弱算法都能第一时间发现。如果环境里没有专门的密钥管理平台用 crontab 加一个脚本也能实现基本效果。脚本内容就是把 ssh-audit 的结果和上次记录的基线做 diff有差异就告警。核心是让算法清单处于可审计状态而不是改完一次就再也不看。6.2 变更记录和监控回溯建议把本次修复的时间、修改的参数、涉及的服务器清单、验证命令和结果都记录下来。这个记录不只是为了应付审计更是为了后续排障时能快速判断这个配置到底是哪次变更引入的。我习惯把这类变更记录放在配置管理仓库里用 Git 做版本跟踪。sshd_config 的每次修改都提交一次配合 commit message 写清楚变更原因和影响面。半年后回看时能非常清楚地还原每次安全加固的决策过程。监控方面可以对 SSH 连接失败率做告警。尤其是因为算法协商失败产生的连接拒绝这类错误在正常运维中不应该出现一旦批量发生说明有设备或脚本没有跟上算法更新节奏。及时处理这些不兼容的连接能避免后续大规模变更时出现意外。还有一个小技巧可以周期性执行一次远端算法扫描对比上一轮结果。如果发现某台服务器的算法列表突然变了首先要查是不是有人手动改了 sshd_config其次要查是不是包管理器更新了 OpenSSH 版本导致默认配置变化。这种被动发现机制能有效兜住低级失误。我自己在几次修复过程中养成了一个习惯每次调整完算法白名单都会顺手把当时的 sshd -T 输出和 ssh-audit 扫描结果存一份快照。等下次做安全整改时翻出这些快照对比当前状态变化一目了然。这套方法在应对等保测评和安全自查时特别好用因为所有证据都是现成的不需要临时去回忆。如果你也在处理类似的 SSH 加固任务建议从体检开始一步步走完整个流程不要跳步这套操作在生产环境里是完全可以落地的。