搞安全运维的兄弟,十有八九都被漏洞扫描报告里那一排SSH条目录折腾过。前两天刚帮同事收尾一起整改,扫描报告里明晃晃写着:CVE-2002-20001,SSH服务端存在不安全的密钥交换算法,建议禁用diffie-hellman-group1-sha1、diffie-hellman-group14-sha1等老旧算法。说实话,这种问题几乎每个季度都会出现一次,有的客户环境甚至把Risk评为High。其实真要动手处理并不复杂,但里面有几个坑,改完连不上、语法检查过了实际没生效、老客户端被锁死……我都遇到过。今天干脆把整个流程掰开揉碎讲清楚,从原理到实操再到自救,全部写成可以直接照着做的版本。不论你是刚接触Linux运维的新人,还是被安全扫描搞得焦头烂额的老手,这篇文章都能帮你把SSH密钥交换算法这件事一次性收拾干净。
1. 先搞清楚病根:CVE-2002-20001到底指向什么问题
1.1 这些“老算法”为什么还活着
很多人看到CVE编号就慌,以为服务器被入侵了。实际上,CVE-2002-20001这一类条目,在绝大多数安全扫描工具里,指向的是同一个事实:SSH服务端仍然向客户端提供不安全的密钥交换算法。所谓密钥交换算法,简单说就是客户端和服务器在建立加密通道之前,互相确认“用哪种数学方式协商出共同密钥”的协议。这个过程决定了初始连接的安全性,如果算法本身有弱点,后续加密再强也没用,因为密钥协商环节已经可能被攻破。
我打一个比方。你和一个陌生人在人群里接头,两人先约定“用暗号A碰头”,结果这个暗号A早被围观群众摸透了,围观者完全可以冒充你的接头对象,先跟你把暗号对完,再把你的话转给真正的对接人,这就是中间人攻击的雏形。diffie-hellman-group1-sha1、diffie-hellman-group14-sha1以及diffie-hellman-group-exchange-sha1这些算法,用的核心哈希是SHA-1,非对称素数群强度不足,前者已经被学术界证实存在碰撞风险,后者的参数生成过程也缺乏足够的随机性保障。说白了,这些算法就是“暗号A”,在2024年的今天,实在不该继续出现在生产环境里。
1.2 攻击路径与真实风险
我不能把问题搞得太玄,尽量讲人话。SSH握手的顺序是:双方先交换KEX算法列表,然后协商出一套双方都支持的算法,再走完密钥交换流程,最后才开始认证。攻击者能做的最典型的事情,就是把自己插在中间,主动把双方支持的算法列表“降级”。你服务器同时支持强算法和弱算法,攻击者就迫使双方都选择弱算法。一旦协商到diffie-hellman-group1-sha1,攻击者在算力足够的情况下,理论上可以破解密钥交换参数,进而解密、篡改通信内容,甚至截获登录凭据。
还有一种常见场景是内网横向移动。攻击者已经拿下一台跳板机,扫描内网其他主机的SSH服务,发现它们仍然支持G1算法,随后发起离线破解或者握手劫持,直接扩大战果。这也是为什么安全扫描报告里,这类问题会被标为High。就算现在没人盯着你的服务器,等哪天出了别的漏洞,这种薄弱点就可能被顺势利用。所以禁用不安全算法,不是为了应付检查,而是为整个内网减少一个容易被利用的面。
1.3 一个容易混淆的细节
做整改前先把概念理清楚。很多人把密钥交换算法、主机密钥算法、MAC算法、加密算法混为一谈,操作时只加了一行KexAlgorithms就以为万事大吉,结果扫描报告里依然显示其他问题。实际上它们是SSH连接中四个不同环节的独立参数:密钥交换算法负责协商会话密钥的方式,主机密钥算法负责服务器身份验证,加密算法负责数据传输加密,MAC算法负责完整性校验。这篇文章的核心是密钥交换算法,但为了让扫描报告彻底干净,我会在后面的配置模板里一并把其他几项也做了。毕竟修复一次,最好一次到位,别再反复折腾。
2. 动手前先盘一盘家底:确认你的SSH现状
2.1 三条命令看清当前配置
不推荐直接改配置,先花两分钟看清楚现状。第一条命令是查看当前OpenSSH支持的密钥交换算法列表,我常用的有两条:
ssh -Q kex这条命令输出的是当前SSH客户端程序支持的KEX算法,注意,它表示的是“能力”,而不是“实际允许使用什么”。
第二条才关键:
sshd -T | grep -i kexalgorithms这条命令会读取当前生效的sshd配置文件,输出服务端实际允许使用的算法列表。如果sshd -T不需要root权限的版本,直接执行即可;某些发行版上需要加sudo。这一步非常重要,因为我经常遇到一种情况:配置文件里写了一大堆,但写错了位置,或者语法有问题导致设置没生效,结果实际运行的算法列表还是默认值。
除此之外再看一眼版本号:
ssh -V为什么要看版本?因为OpenSSH 7.6以下的老版本,对某些新算法的支持是缺失的,比如curve25519-sha256,老版本不一定在支持列表里。如果你在旧版本上照抄新版配置模板,重启后sshd可能直接报“algorithm not found”,服务都起不来。先确认版本,再决定配置模板怎么组合,这是避免事故的第一步。
2.2 发行版和场景差异要注意
实际操作中,不同发行版对默认算法的处理不一样。我在某主流企业级发行版上测试过,OpenSSH 8.x版本默认已经不包含group1系列;但一些老系统、嵌入式设备以及第三方定制的SSH服务,可能还保留这些老旧算法。这就是为什么扫描报告里来自不同IP的SSH告警详细程度不一样,有的只报group1,有的连group14也一起报。
另外还有一个容易忽略的点:客户端环境。云平台自带的网页版远程连接,很多走的是WebSocket加自研网关,再转发到SSH端口;堡垒机系统往往内置了一套OpenSSH兼容逻辑,这些中间组件如果版本偏老,只支持group14之类算法,你这边一禁用,所有从堡垒机发起的连接可能马上失败。我在做某公司整改时,就发生过类似的“墙内开花墙外红”的惨剧,后面我会专门讲这个场景。所以动手之前,建议先摸清楚:
- 当前SSH服务版本
- 发行版内置默认策略
- 是否有堡垒机、跳板机、云平台Web Shell等中间链路
- 是否还有老系统客户端需要连接
想清楚这几点,后面的配置才有把握。
3. 正式开干:配置禁用并加固SSH
3.1 核心配置:KexAlgorithms一行搞定
先说纯密钥交换算法的禁用。在/etc/ssh/sshd_config里加上这一行:
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256这一行的含义是:只允许列出的这五种算法进行密钥交换,其他算法一律拒绝。curve25519-sha256是当前推荐度最高的算法,速度极快,安全性强;diffie-hellman-group16-sha512和diffie-hellman-group18-sha512是强度较高的传统DH算法,兼容性也不错;group-exchange-sha256则是作为老客户端和新配置之间的过渡项。没有把diffie-hellman-group14-sha1、diffie-hellman-group1-sha1加进去,是从根本上移除问题。
如果你追求更严格的配置,甚至可以把group-exchange-sha256也去掉,只留curve25519和group16以上。我一般建议,如果线上客户端全是新版本,就去掉group-exchange-sha256;如果还有零星老客户端,保留它算是妥协方案。但这里有一条底线:group14-sha1和group1-sha1绝对不留。
3.2 顺手加固其他环节
为了扫描报告一次通过,我把MAC算法、主机密钥和加密算法也一并优化。MAC算法这部分,我建议这样写:
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256这样一来,所有基于SHA-1的hmac-sha1、hmac-md5系列全部被禁用。etm后缀代表encrypt-then-mac模式,安全性优于传统的mac-then-encrypt,虽然现代系统默认已经倾向etm,但显式配置一遍更放心。
主机密钥算法这一行:
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com关键是把ssh-rsa(基于SHA-1的RSA签名)去掉。很多设备默认还用ssh-rsa做主机认证,一旦服务器主机密钥是RSA,客户端又会默认用ssh-rsa签名方式验证,扫描器照样标记。改成只允许ed25519和RSA-SHA2之后,既能保证现代客户端无障碍连接,又把老签名方式挡在门外。
加密算法这一行:
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctrCBC模式(比如aes128-cbc、aes256-cbc)全部去掉,只保留CTR和AEAD类算法。CBC模式历史上出现过若干理论攻击,禁用之后扫描报告里常见的弱加密算法告警也会消失。
把四行配置放到一起,看起来是这样:
# 原始备份在 /etc/ssh/sshd_config.bak.20240101 KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256 MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256 HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr这里特别提醒:不要直接在原文件上删改旧行,最好在文件末尾追加这几行,并用注释方式把旧的默认项保留下来。这样万一要回退,把新行注释掉就能恢复。还有一个习惯值得养成:在sshd_config所在目录建一个conf.d子目录,用Include /etc/ssh/sshd_config.d/*.conf的方式管理配置片段。后者好处是升级系统时不容易被覆盖,排查时思路也清晰。
3.3 验证、重载与线上检查的完整流程
配置写完了,先别急着重启,按顺序来:
sshd -t这一步是语法检查。如果有拼写错误、不认识的算法名,这里会直接报错,比如Unsupported option ...或Bad SSH2 KexAlgorithms ...。语法通过后再执行:
systemctl reload sshd这里我强烈建议用reload而不是restart。reload是让服务重新读取配置文件,不断开现有连接;restart则是完全重启,会把当前所有在线会话全部踢掉。生产环境下一旦踢掉了正在跑任务的会话,后果很可能是正在执行的数据迁移中断、长任务丢失。所以能用reload绝不用restart。
重载完成之后,立刻检查实际生效的算法:
sshd -T | grep -i kexalgorithms这一步必须做。如果看到输出是你新配置的那一串,说明生效成功;如果还是原来的默认值,可能是配置文件路径不对、或者配置被后面出现的相同指令覆盖了。OpenSSH的配置规则是“第一个匹配项生效”,不同版本有差异,但保险的做法是确保整个配置里不要出现两处KexAlgorithms,否则容易出现预期之外的覆盖。
最后,从客户端侧验证一下。随便找一台堡垒机或者跳板机,尝试连接目标服务器:
ssh -vvv root@目标IP如果看到kex: algorithm: curve25519-sha256,说明协商正常。然后强制用旧算法测试:
ssh -v -o KexAlgorithms=diffie-hellman-group14-sha1 root@目标IP如果目标服务器配置正确,这条命令会立刻报错:Unable to negotiate with 目标IP port 22: no matching key exchange method found. Their offer: curve25519-sha256,...。看到这行报错就说明旧算法已经被成功禁用。这就是最直观的验证方式。
4. 排查实录:改完SSH连不上的自救指南
4.1 最常见的“改完就掉线”事故
这类操作最有戏剧性的翻车现场就是:配置文件改完,reload一执行,当前SSH会话直接卡住,新连接也进不来。第一次遇到这种状况的人,冷汗直接冒出来。我复盘过很多次,最常见的根因有三个:
第一,配置文件权限错误。sshd对配置文件权限要求非常严格,普通用户可写的配置文件会被直接拒绝加载。可以用ls -l /etc/ssh/sshd_config确认,权限必须是644,属主root。如果之前有人手动编辑后把权限改成了664或更高,sshd启动时直接拒绝执行,服务起不来。
第二,算法列表拼写错误。比如把diffie-hellman-group16-sha512拼错成diffie-hellman-group16-sha51,语法检查不通过,服务重载失败。这时候你原来的SSH进程还在运行,新连接也没断,但要等到下次重启或者服务挂掉才会暴露问题。
第三,老系统不支持新算法导致协商失败。我在一台老系统上实验时,把KexAlgorithms设置为curve25519-sha256、group16-sha512和group18-sha512,结果客户端那边的SSH版本太老,根本不认识这些算法,协商直接失败。表现就是:ssh -vvv输出里显示no matching key exchange method。
遇到连不上的情况,第一件事是保持冷静,立刻改回备份文件。这就是备份的意义。如果还能登录系统,马上把/etc/ssh/sshd_config.bak覆盖回来,然后reload。如果当前会话已经断了,就得靠带外管理方式:云平台的控制台VNC、物理机的IPMI、虚拟化平台的串口控制台,这些都是绕过SSH的登录途径。只要系统层面没坏,通过这些入口进入就能恢复。
4.2 老客户端与老系统的兼容性取舍
我理解很多运维不想禁用旧算法,纯粹是因为担心某些老服务器连不上。但拿本世纪二十年代初还在跑CentOS 6、Windows 7遗留系统的环境举例,这些系统的OpenSSH客户端默认首选算法确实可能是diffie-hellman-group14-sha1。如果目标服务器把group14全部禁掉,这些老客户端默认就抓瞎了。
但这事有解。客户端可以通过命令行参数临时指定算法:
ssh -o KexAlgorithms=diffie-hellman-group14-sha1 user@host如果你的所谓“老系统”只是偶尔用一次,完全可以在连接时加上这个参数,而不是让服务器长期开放弱算法。我在某公司优化时,就是把服务器端策略收紧,然后给几台老机器写了个别名:
alias sshop='ssh -o KexAlgorithms=diffie-hellman-group14-sha1 -o MACs=hmac-sha1'这样一来,老系统的日常操作不受影响,服务器端的安全策略也能保持整洁。唯一需要确认的是,这条通道只在受控网络内使用,不要让弱算法暴露给不可信网络。
还有一个中间件场景要格外小心:堡垒机。某次整改,我按标准配置把服务器端算法收紧,结果堡垒机所有的代填连接全部失败。后来发现堡垒机的SSH组件是瘦身定制版,为了兼容各种老设备,默认只会用group14这一档做协商。解决的办法不是在服务器端妥协,而是升级堡垒机自身组件,或者在其连接配置里显式指定现代KEX算法。这件事给我们的启示是:做改动前,必须把链路中间层的兼容性测试纳入计划,不然整改就会变成事故。
4.3 日志排查法
如果修改后出现连接异常,日志是最好用的“照妖镜”。主流系统的SSH日志位置一般在:
/var/log/secure(某RHEL系发行版)/var/log/auth.log(某Debian系发行版)
用journald的系统也可以直接看:
journalctl -u sshd --since "10 minutes ago"典型报错长这样:
Unable to negotiate with 192.168.1.10 port 52678: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie-hellman-group1-sha1这段几乎是“新手维修指南”。它告诉我们,客户端只提供了这两种被禁用的算法,服务器没有任何共同选项,于是拒绝连接。看到这个,处理思路就很清晰:要么客户端升级/指定新算法,要么服务器临时放宽。生产环境建议先放宽,确认业务影响,再审慎收紧。
如果日志里出现:
kex_exchange_identification: Connection closed by remote host这类情况多半不是算法配置引起的,而是TCP层直接被拒,可能跟防火墙、fail2ban、tcp_wrappers有关。这是另一个排查方向,不要盯着算法配置死磕。
4.4 快速回滚与灾难恢复
我自己的惯例是,任何配置变更之前,强制做三件事:
- 备份当前配置:
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%Y%m%d%H%M%S)- 把回滚命令提前写好,放在终端历史或者本地文档里:
cp /etc/ssh/sshd_config.bak.时间戳 /etc/ssh/sshd_config && systemctl reload sshd- 确认自己持有带外管理权限。不管是云控制台、虚拟化平台还是物理机的管理口,确保修改SSH之前能通过这些渠道登录系统。没有带外权限就动SSH配置,等于闭着眼睛换飞机引擎。
额外提示:不要在业务高峰期做这类改动。多数SSH配置变更如果引发问题,通常在几分钟内就会暴露。选择低峰期操作,能给自己留足处置时间。
5. 踩坑之后的几点实在建议
5.1 别拿别人的模板无脑复制
网上的安全加固文档,很多都会给你一份“终极模板”。但照搬前一定看清楚版本适用范围。我吃过亏:在OpenSSH 7.4版本的老环境里,直接复制了只包含curve25519-sha256和group16-sha512的配置,结果服务起不来。原因是这个老版本连这些算法的名字都不认。不同OpenSSH版本支持的算法集合完全不一样,照抄之前务必用ssh -Q kex确认本地支持列表,再调整模板。
5.2 定期巡检比修补更省事
这类问题之所以反复出现,主要是“改完一次就不管了”。系统升级、应用安装、有人手动恢复配置文件,都可能导致算法设置被重置。我建议每季度做一次SSH安全巡检,用顺手的小工具就行。比如用ssh-audit,一行命令就能把服务器支持的算法、密钥、MAC、加密项全部拉出来并打上风险标记。如果没有现成工具,也可以定期保存一下sshd -T的输出做对比,发现偏离立即处理。
5.3 配置管理工具是最终解
如果你管理的服务器超过十台,手工逐台改SSH配置绝对是噩梦。我后来把所有SSH加固参数都收进了统一的配置管理仓库,通过自动化工具下发到每一台主机。参数模板集中在同一个文件里,版本变更、批量修改都能快速执行,还能保证每台机器配置一致。人工从来没有自动化可靠,安全整改尤其如此。
这次把CVE-2002-20001相关的整改过程完整记录下来,也是给自己留个存档。SSH算法加固这事,技术含量并不高,但细节里的坑一个比一个隐蔽。只要你按照先摸底、再配置、后验证、留后路的流程走一遍,基本不会出大问题。等到扫描报告再次出来,你可以从容地把干净的结果截图发出去,不用再来回折腾了。