SSH弱加密算法排查与修复实战:从Nmap扫描到sshd_config加固 经常有朋友发来一张安全扫描报告截图里面赫然写着“SSH Server supports weak algorithms”或者“CBC Ciphers Detected”紧接着一堆CVSS分数和“High”严重度。第一次遇到的人容易懵SSH不是加密的吗弱算法到底是什么意思我应该直接改配置还是只能干瞪眼这个问题其实不难难的是很多人不知道该用哪条命令去核实也不知道改完会不会把自己锁在服务器外面。这篇内容就专门解决这两个痛点。我会从SSH协商算法的基本原理讲起列出哪些算法属于“弱算法”以及它们为什么会被判弱然后给出完整的Nmap检测命令和输出解读方法再手把手教你改sshd_config把算法白名单收紧最后附上修复后的验证流程和常见连接失败排查思路。适合所有Linux服务器管理员、安全运维和应用部署同学哪怕你之前没听过“算法协商”这个词跟着走一遍也能落地。1. 弱加密算法问题全貌为什么几十行配置会引发安全告警1.1 SSH协商了什么算法清单就是你的攻击面先花点时间搞清楚SSH在连接时做了什么。SSH协议不是“客户端发密码、服务器验证”这么简单真正连接时三步跑完传输层握手、用户认证、连接复用。第一步就牵扯到算法协商双方必须找到一个都能支持的密钥交换算法、加密算法、完整性校验算法和主机密钥类型然后才进入后续流程。服务器在sshd_config里配置的Ciphers、MACs、KexAlgorithms、HostKeyAlgorithms就是它愿意接受的算法清单。只要客户端发起连接SSH会自动把服务器支持的算法列表暴露给对端。问题是这个协商过程发生在认证之前扫描器根本不需要知道密码只要TCP端口是开的它就能通过发送一个协议握手包把服务器承认的所有算法全部拿回来。也就是说你的SSH服务暴露了多少种算法就相当于暴露了多少个潜在的利用入口。安全扫描器判断你“存在弱加密算法”本质上就是拿到了这张清单并和它自己的弱点规则库做了比对。所以第一步永远是搞清楚服务器当前到底宣告了哪些算法而不是凭印象去猜。1.2 哪些算法算“弱”它们到底弱在哪先说几个常见的“弱算法典型”。第一个是CBC模式加密算法比如aes128-cbc、aes256-cbc、3des-cbc、blowfish-cbc。CBC模式本身不是不能用于SSH但在SSH协议的特殊实现下出现过预言机攻击风险社区和各安全基线都倾向拒绝使用。第二个是RC4也就是arcfour、arcfour128、arcfour256。RC4早已被证明存在系统性偏置相关攻击可以逐步恢复明文内容属于必须删除的算法。第三类是SHA-1相关的HMAC例如hmac-sha1、hmac-sha1-etm。SHA-1的碰撞攻击已经成为现实威胁在完整性校验场景中虽然直接利用难度较高但安全扫描器仍会标记。第四类是短密钥Diffie-Hellman比如diffie-hellman-group1-sha11024位、diffie-hellman-group14-sha12048位但依赖SHA-1弱DH参数可能让会话密钥被预计算攻击破解。第五类是ssh-rsa主机密钥签名算法它本质上是SHA-1RSA所以现在OpenSSH的推荐做法是改用rsa-sha2-256或rsa-sha2-512。这些算法不是说你一启用服务器立刻就会被攻破。更多时候它们是“历史遗留问题”早期SSH为了兼容各种老客户端默认把能用的算法全开。随着密码学进展这些算法逐步从“可用”变成“不可接受”。安全基线更新后扫描器自然会把它们标红。真正需要建立的认知是SSH配置不是越宽松越好而是能明确拒绝的算法就坚决拒绝。2. 宿敌在手用Nmap和辅助工具摸清家底2.1 Nmap扫描一条命令拿到协商算法清单先给结论最常用的检测命令是nmap -sV --script ssh2-enum-algos -p 22 192.0.2.10如果你有多个端口或者SSH跑在非标准端口比如2222就把-p 22换成-p 2222。如果不确定端口可以用-p 22,2222,22222。命令拆开看-sV让Nmap对SSH服务做版本探测确认远程确实是SSH服务并且能拿到版本号。--script ssh2-enum-algos调用NSE脚本库中的ssh2-enum-algos脚本它会主动完成SSHv2的算法协商流程并记录服务器在SSH_MSG_KEXINIT消息中宣告的算法。-p 22指定端口。执行完输出大致长这样我用一个模拟环境的实际结果整理出来PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.4 (protocol 2.0) | ssh2-enum-algos: | kex_algorithms: (8) | curve25519-sha256 | curve25519-sha256libssh.org | diffie-hellman-group-exchange-sha256 | diffie-hellman-group14-sha1 | diffie-hellman-group14-sha256 | diffie-hellman-group1-sha1 | diffie-hellman-group-exchange-sha1 | ecdh-sha2-nistp256 | ecdh-sha2-nistp384 | server_host_key_algorithms: (3) | ssh-rsa | rsa-sha2-256 | rsa-sha2-512 | encryption_algorithms: (8) | aes128-ctr | aes192-ctr | aes256-ctr | aes128-cbc | aes256-cbc | 3des-cbc | arcfour | arcfour128 | mac_algorithms: (6) | hmac-sha1 | hmac-sha2-256 | hmac-sha2-512 | hmac-md5 | hmac-sha1-etm | hmac-sha2-256-etm |_ compression_algorithms: (2) | none | zlibopenssh.com这张表里ssh-rsa、aes128-cbc、3des-cbc、arcfour、hmac-sha1、diffie-hellman-group1-sha1都是需要处理的。单纯看输出不够直观我建议你把扫描结果保存到文件里nmap -sV --script ssh2-enum-algos -p 22 192.0.2.10 -oN ssh_scan_before.txt之后做修复前后对比时这个文件就是最直接的证据。另外如果只想看主机密钥指纹可以追加一个脚本nmap -sV --script ssh2-enum-algos,ssh-hostkey -p 22 192.0.2.10ssh-hostkey会返回RSA/ECDSA/Ed25519公钥的SHA256指纹方便你确认主机密钥类型。2.2 本机自查ssh -Q 与辅助工具双保险Nmap能扫远程机器但拿到服务器本身权限时更快的办法是在本机直接查看SSH支持哪些算法。OpenSSH 7.3以上的版本提供了ssh -Q命令专门用于查询本地SSH支持的算法列表# 查看支持的加密算法 ssh -Q cipher # 查看支持的MAC算法 ssh -Q mac # 查看支持的密钥交换算法 ssh -Q kex # 查看支持的主机密钥类型 ssh -Q key需要注意ssh -Q列出的是当前OpenSSH编译时支持的全部算法不一定是sshd_config实际启用的算法。如果你要确认实际生效的配置需要看sshd_config里的设置。但用ssh -Q可以快速确认某算法是否还存在于软件库中比如装完新版OpenSSH后旧算法可能已经被上游移除这种情况下你甚至不需要在配置文件里手动禁用。还有两个很好的辅助工具。第一个是ssh-audit它是个开源Python脚本不需要安装依赖直接下载就能跑。它的输出比Nmap更贴心会把算法分颜色标记为“良好”“弱”“不安全”。用法非常简单python3 ssh-audit.py 192.0.2.10它会自动识别OpenSSH版本逐个检查密钥交换、加密算法、MAC、主机密钥最后给出加固建议。第二个是cipherscan一个更老的脚本也能列出算法但维护已经不太活跃。我推荐主要用Nmap和ssh-audit这两个足够覆盖绝大多数排查场景。实际排查顺序我建议这样先nmap扫描所有目标机器拿到统一的算法清单再对有问题的机器登录上去用ssh -Q确认本地算法池最后用ssh-audit生成一份格式化的审计报告方便归档。3. 实战修复把sshd_config改成白名单机制3.1 修复前必须做的备份与连接保护在动sshd_config之前务必要做好三件事。第一备份原始配置第二保持当前SSH连接不要断开第三验证语法是否正确。备份可以用时间戳文件比如sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date %F)如果你的服务器使用了云厂商的“自定义配置”或者Puppet/Ansible管理那就把这份变更提交到配置仓库方便回滚。保持连接不断开是因为修改SSH配置后需要重启服务而重启过程中万一配置写错你只有一个还活着的会话能用作为救命的退路。很多人在修复弱加密算法时把自己锁在门外多数就是因为单会话直接重启。改完配置后先检查语法sudo sshd -t这条命令不会真正启动sshd只做配置解析。如果语法有问题它会直接报错比如“unexpected token”。确认没问题后再执行sudo systemctl restart sshd或者旧式SysV init系统用sudo service ssh restart重启后不要立刻关掉当前窗口先打开一个新终端尝试连接确认能登录成功后再关旧会话。3.2 核心参数配置Ciphers、MACs、KexAlgorithms、HostKeyAlgorithms现在进入正题。在/etc/ssh/sshd_config末尾追加以下几行# 禁止明文密码之外的弱算法残留 KexAlgorithms curve25519-sha256,curve25519-sha256libssh.org,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256 Ciphers chacha20-poly1305openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcmopenssh.com,aes256-gcmopenssh.com MACs hmac-sha2-512-etmopenssh.com,hmac-sha2-256-etmopenssh.com,hmac-sha2-512,hmac-sha2-256 HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01openssh.com,rsa-sha2-256,rsa-sha2-512,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521逐条解释这些配置是干什么的以及为什么这样选。KexAlgorithms密钥交换算法。它决定了会话密钥怎么通过DH或ECDH协商出来。curve25519-sha256性能好且安全现代OpenSSH默认首选diffie-hellman-group-exchange-sha256使用动态生成的DH参数group16和group18是4096位和8192位固定组安全性强。我排除了diffie-hellman-group1-sha1和diffie-hellman-group14-sha1前者是1024位短密钥后者虽然位数够但SHA-1不再可靠。Ciphers对称加密算法。chacha20-poly1305openssh.com是流加密同时带认证性能极好尤其适合无AES硬件的CPUaes*-ctr是计数器模式不泄露明文模式特征aes*-gcm是带认证的加密模式安全性和性能都不错。我把所有*-cbc和arcfour*全部排除。MACs消息认证码。hmac-sha2-256和hmac-sha2-512是SHA-2家族强度足够带-etm后缀的是“先加密后MAC”模式能避免一些把密文和MAC绑定不足的问题。我排除了hmac-md5和hmac-sha1。HostKeyAlgorithms服务器用来证明自己身份的主机密钥算法。ssh-ed25519是当前最推荐的曲线签名RSA签名改用rsa-sha2-256/rsa-sha2-512这是OpenSSH 7.2以后支持的SHA-2签名变体。我把ssh-rsaSHA-1签名排除了。这四行实际上是一种“白名单”思想不再逐个列禁用项而是只放行确定安全的算法。以后就算OpenSSH版本升级新增了某些算法只要没写进白名单它也不会被启用。有一点值得说明配置中算法顺序也影响协商结果客户端支持多种算法时服务端会按自己配置的优先级从高到低匹配。所以最好把curve25519系列放最前面aes256-gcm放靠前位置。如果你不确定某个算法在你当前OpenSSH版本里是否被支持就用ssh -Q查一下不支持的写进去会直接导致sshd无法启动。3.3 升级与辅助加固OpenSSH版本、moduli文件与主机密钥配置白名单只是第一步建议同时做三件辅助工作。第一升级OpenSSH。老版本OpenSSH的问题不只是默认算法旧还可能存在历史CVE。比如OpenSSH 7.4及更早版本在很多环境里已经不再符合安全基线。如果系统是CentOS 7这类不便升级的场景至少把sshd_config里的算法收紧再考虑通过软件集或容器方式运行新版OpenSSH。我这里不展开具体升级路径但你要知道算法白名单只能控制“能用什么”版本升级才能控制“还有哪些未修复漏洞”。第二更新DH参数。如果你启用了diffie-hellman-group-exchange-sha256服务器的DH模数moduli质量很重要。OpenSSH发行版自带一个/etc/ssh/moduli文件里面是预生成的DH质数默认通常足够。但有些精简版系统可能没有这个文件或者文件里质数长度偏弱。可以用以下方式重新生成# 生成候选质数时间可能较长 ssh-keygen -G /etc/ssh/moduli.all -M 2048 # 筛选并写入正式moduli文件 ssh-keygen -T /etc/ssh/moduli -f /etc/ssh/moduli.all注意-G生成过程可能持续数分钟甚至更久适合在维护窗口内执行。如果不想等也可以直接删除/etc/ssh/moduli文件OpenSSH会回退到内置算法但效果不如自己生成。对大多数内网环境保留系统自带的moduli文件也够用。第三管理主机密钥。检查一下/etc/ssh/下有哪些主机密钥文件sudo ls -l /etc/ssh/ssh_host_*_key*理想情况下你应该有ssh_host_ed25519_key、ssh_host_rsa_key最好还有ssh_host_ecdsa_key但不建议继续生成1024位DSA密钥。如果发现ssh_host_dsa_key存在直接删除并在配置里确保没有引用它。如果只有老旧的RSA-2048也可以生成新的Ed25519密钥sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N 生成后重启sshd即可。之后客户端再次连接时会看到主机密钥指纹变化属正常现象需要在客户端清理旧的known_hosts记录。4. 修复后验证与常见问题排查别把自己锁在门外4.1 如何验证修复已经生效Nmap复查与ssh连接测试改完配置并重启sshd之后立刻在原扫描命令上重新跑一遍nmap -sV --script ssh2-enum-algos -p 22 192.0.2.10 -oN ssh_scan_after.txt这次输出应该清爽很多模拟结果如下PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.4 (protocol 2.0) | ssh2-enum-algos: | kex_algorithms: (4) | curve25519-sha256 | curve25519-sha256libssh.org | diffie-hellman-group-exchange-sha256 | diffie-hellman-group16-sha512 | server_host_key_algorithms: (3) | rsa-sha2-256 | rsa-sha2-512 | ssh-ed25519 | encryption_algorithms: (5) | chacha20-poly1305openssh.com | aes128-ctr | aes192-ctr | aes256-ctr | aes128-gcmopenssh.com | aes256-gcmopenssh.com | mac_algorithms: (4) | hmac-sha2-256-etmopenssh.com | hmac-sha2-512-etmopenssh.com | hmac-sha2-256 | hmac-sha2-512 |_ compression_algorithms: (2) | none | zlibopenssh.com拿这个结果和修复前对比你已经看不到任何cbc、arcfour、sha1、group1的标志。如果你还想确认实际连接时协商到了哪个算法可以使用SSH的调试模式ssh -vvv user192.0.2.10在输出的中段会看到类似“Using SSH_MSG_KEX_ECDH_INIT”和“debug1: kex: algorithm: curve25519-sha256”这样的日志接着会显示“debug1: cipher: chacha20-poly1305openssh.com”和“debug1: mac: hmac-sha2-512-etmopenssh.com”。这比Nmap扫出来的更真实因为这是实际建立连接最终选用的组合。还可以直接把ssh-audit的输出存成报告重点看它最后的“整体评分”。修复前是“Warnings”修复后会变成“Good”或“Excellent”这份报告发给安全团队或者放进合规材料里都很有说服力。4.2 常见连接失败与应对方案修复SSH算法后最常遇到的问题是服务器配置改完了结果某些老客户端连不上了。常见报错有下面几种。“Unable to negotiate with ... no matching key exchange method found. Their offer: diffie-hellman-group1-sha1” 这说明客户端的SSH版本旧只支持很老的Diffie-Hellman密钥交换方式而你已经在服务端禁掉了。临时应急办法是手动指定算法连接ssh -oKexAlgorithmsdiffie-hellman-group14-sha1 user服务器IP注意这个号表示“在客户端默认算法基础上追加这一项”而不是只使用这一项。这只适用于临时排除问题不要长期依赖。更稳妥的解法是升级客户端的OpenSSH到支持现代算法的版本。如果某些嵌入式设备实在无法升级那就只能在该设备对应的网络边界做访问控制限制它只连特定跳板机而不是把服务器整体拉回弱算法阵营。“no matching cipher found. Their offer: aes128-cbc” 处理思路和上面一样。可以在客户端临时用ssh -oCiphersaes128-cbc user服务器IP但同样只是临时下载文件的场景。长期方案是给这些终端设备换用支持CTR或GCM模式的SSH客户端或者改用SFTP替代某些老脚本。“Connection closed by remote host” 重启sshd后如果配置里有不存在的算法名称或者某个参数被拼错服务端可能已经启动失败。先看一眼服务状态sudo systemctl status sshd如果显示“failed”用sudo sshd -t找到具体语法错误。如果服务已启动但连接被关闭再检查/var/log/secure或journalctl -u sshd里的“fatal”日志。“Host key verification failed” 前面提到如果你生成了新的主机密钥客户端known_hosts里旧指纹会不匹配。解决方案是在客户端执行ssh-keygen -R 服务器IP然后重新连接确认新指纹无误后输入yes记录。“Invalid MAC algorithm” 如果某些Windows自带SSH客户端版本特别老可能连接不上。可以临时在客户端指定老MAC算法但更建议换用新版本客户端或PowerShell的OpenSSH。把这些场景做一个汇总你就能快速定位问题先确认是哪一端的算法池不匹配再决定是临时-o参数绕过还是彻底升级客户端。4.3 自动化巡检一条Shell命令定期检查算法清单修复一次很容易但想长期不复发就需要把检测工具化。我自己习惯用一个小脚本批量扫描多台服务器并把结果统一存档。下面这个脚本可以放在/usr/local/bin/ssh_algo_scan.sh#!/bin/bash # 需要巡检的服务器IP列表每行一个 HOSTS_FILE/etc/ssh_audit_targets.txt TIMESTAMP$(date %F_%H%M%S) OUTPUT_DIR/var/log/ssh_algorithm_scan mkdir -p $OUTPUT_DIR while read -r HOST; do if [ -z $HOST ]; then continue fi echo Scanning $HOST ... nmap -sV --script ssh2-enum-algos -p 22 $HOST \ -oN ${OUTPUT_DIR}/${HOST}_${TIMESTAMP}.txt done $HOSTS_FILE find $OUTPUT_DIR -type f -name *_${TIMESTAMP}.txt -exec grep -lE cbc|arcfour|hmac-sha1|group1 {} \;脚本逻辑很简单读取IP列表逐个Nmap扫描并保存结果最后把包含弱算法特征的结果文件列出来。你可以再加一行把这个脚本放进crontab0 2 * * 1 /usr/local/bin/ssh_algo_scan.sh每周一凌晨两点自动扫一遍早上打开邮件看到结果就好。这样就算有人临时手改配置把CBC加回去也能在下次扫描时暴露出来。这里有个经验靠人记配置不靠谱靠脚本周期扫描才稳定。尤其是几个人共用服务器的时候某位同事为了临时兼容旧工具可能直接加一行Ciphers aes128-cbc如果没有自动巡检这个改动可能藏很久不被发现。Nmap的输出文件是天然的审计记录建议扔进Git仓库保存版本历史长期积累后还能回溯“哪一天哪个IP开始出现了弱算法”。最后再分享一点经验修复SSH弱加密算法这件事最危险的反而不是改配置而是改完之后没有验证、没有回滚方案。我第一次整理服务器的时候就踩过一个坑以为自己写的是正确白名单结果sshd -t虽然通过但有一台老客户端在重连时直接报“no matching key exchange method”当时现场没有别的通道只能靠机房带外控制台救回来。后面养成了习惯任何SSH变更之前一定先开双会话并且把配置文件的Git基线更新好。如果你在这个基础上还想更进一步可以关注sshd_config里的PermitRootLogin、PasswordAuthentication和AllowUsers配置。算法白名单解决的是“能不能安全地加密连接”而认证策略解决的是“谁能连进来”。把两者结合SSH服务的安全性才算真正闭环。最后一个小技巧把修复后的Nmap扫描结果重命名为ssh_scan_clean.txt留档的同时贴到工单记录里以后做等保或者安全评审时可以直接复用节省不少沟通成本。