开头
做过站点运维的人应该都有印象,用各种在线扫描工具或本地安全脚本给网站做体检时,经常蹦出一条“服务器支持不安全的RSA密钥交换套件”之类的告警。不少站长一开始没当回事,直到被甲方、被上级或者被自己强迫症逼到墙角,才发现宝塔面板默认的SSL配置里,确实藏着一批基于RSA密钥交换的老旧加密套件。这篇文章围绕“宝塔面板去掉SSL的RSA解决密钥交换漏洞”这件事,把原理、配置、验证、坑全捋一遍,适合接管了宝塔服务器、又不想整天被扫描报告追着跑的运维同学,也适合刚接触SSL配置、想彻底弄明白加密套件是怎么选出来的新手。
先说结论:这不是什么疑难杂症,核心就三步——看懂套件列表、把RSA密钥交换类套件从配置里移除、重载服务并验证。麻烦的是中间涉及不少细节,比如Nginx和Apache写法不一样、改完可能影响老设备的访问、以及某些套件看似换了名字其实还是RSA的变体。别急,下面逐步展开。
1. 先搞清楚:RSA密钥交换漏洞的来龙去脉
很多人在第一步就被劝退了,因为不理解为什么明明用的是正规证书,扫描工具却说有漏洞。这其实和证书没关系,而是你的服务器在TLS握手时,提供给客户端的“可选算法”里包含了几种不安全的密钥交换方式。要彻底解决,得先明白这些算法为什么危险、宝塔为什么默认开启。
1.1 TLS握手里的密钥交换到底干了什么
TLS握手的目标之一,是让客户端和服务端在不安全的信道上协商出一个共同的会话密钥。常见的方式有两种:一种是基于RSA的密钥交换,客户端生成一个预主密钥,用服务器的RSA公钥加密后发给服务器,服务器用私钥解密得到预主密钥;另一种是Diffie-Hellman系列,客户端和服务端各自生成临时参数,通过数学运算各自推导出同样的会话密钥,即使抓包也得不到私钥。
RSA密钥交换最要命的问题,在于“前向保密”缺失。原理不复杂:客户端加密预主密钥时用的是服务器RSA公钥,那如果攻击者长期抓包,把所有流量都记录在案,之后某一天拿到了服务器私钥,就能把历史流量全部解密。相比之下,基于ECDHE的密钥交换每次会话都用临时密钥,历史流量不会被一次私钥泄露拖下水。扫描工具报“RSA密钥交换漏洞”,就是在提醒你服务器允许了这种“一次性加密,永久泄露”的握手方式。
1.2 宝塔为什么会默认启用不安全的套件
很多用户以为宝塔会默认给出最安全的配置,其实不然。宝塔面板的默认SSL配置,主要考虑的是最大兼容性,尤其要保证一些老浏览器、老微信内置浏览器、老旧Android WebView能正常打开页面。这些老客户端不支持新式ECC套件,服务器若不提供RSA套件,客户端握手就会失败,页面直接打不开,投诉接踵而至。所以宝塔默认的ssl_ciphers列表里,保留了一长串RSA开头的套件,同时为了兼容IE11这类古董,甚至保留了TLSv1协议。
提示:理解这一点很重要——不是宝塔故意留后门,而是安全与兼容的天平长期倒向了兼容。这也是为什么很多扫描报告把宝塔站标成“B级”甚至“C级”,而你自己打开浏览器却毫无感知的原因。
1.3 这个漏洞的实际影响范围
被扫描工具标记的常见原因包括:服务器启用了TLS_RSA_WITH_AES_128_CBC_SHA这类套件,或SSLv3、TLSv1.0等过时协议。具体的攻击面有两种:一种是Bleichenbacher类攻击,利用RSA填充校验的差异,通过多次精心构造的握手尝试,逐步还原出被加密的预主密钥,业界2018年公开过的新变种(CVE-2018-0886)就属于这类;另一种是ROBOT攻击,本质上是利用了部分服务器实现中存在的RSA密钥交换行为差异,成功的话同样能够恢复会话密钥。
需要说明的是,这两种攻击利用门槛都不低,实际被攻击的案例不多,但安全扫描不会管概率,只要检测到特征就会报漏洞。如果你是做政企项目、金融类站点,或者要过等保测评,这类漏洞基本是必改项。即便只是自建博客,看到扫描报告里一串“HIGH severity”的字样也相当难受,改掉它顺带还能把评分刷上去,属于性价比极高的加固操作。
2. 动手之前:先明确安全的套件配置长什么样
不要一上来就复制网上的长串cipher字符串,也别直接抄别家服务器的配置。你得先理解现代加密套件体系,再根据自己服务器的OpenSSL版本和站点类型做裁剪。否则轻则配置报错,重则“全站打不开”也没找到原因,我在排查记录里见过太多这类案例了。
2.1 现代加密套件的命名规律
OpenSSL的套件名称由几部分组成,比如“ECDHE-RSA-AES128-GCM-SHA256”这段文字可以拆成四段来看:密钥交换算法(ECDHE)、身份认证算法(RSA)、对称加密算法(AES128-GCM)、摘要算法(SHA256)。其中最关键的是第一段的密钥交换算法。
我的建议是,配置里只保留ECDHE开头的套件,因为ECDHE是“临时椭圆曲线Diffie-Hellman”,具备前向保密;认证部分保留RSA或ECDSA都行,这取决于你的证书类型;对称加密部分优先AES-GCM和CHACHA20,认证哈希优先SHA256及以上。至于“DHE-RSA-AES128-GCM-SHA256”这类套件,虽然也提供前向保密,但DHE握手延迟大、性能差,除非要兼容某些极端老旧的客户端,否则没有保留的必要。
2.2 不同Web服务的配置差异
宝塔面板下最常见的是Nginx和Apache两种环境,二者关于加密套件的配置参数有差异但原理一致。Nginx使用ssl_ciphers指令,Apache使用SSLCipherSuite指令,语法都以冒号分隔。另外Nginx有ssl_prefer_server_ciphers on这个开关,控制服务器端是否优先套用自己定义的套件顺序;Apache则通过SSLHonorCipherOrder On实现同等效果。
最大的差异在于配置位置。Nginx既可以在“站点配置文件”里设置,也可以在“Nginx主配置文件”里设置;Apache则通常在“站点配置文件”中配置SSL虚拟主机段落。宝塔面板的图形界面帮你改了证书,但不一定帮你改套件,得在“网站-设置-配置文件”里手动添加。具体路径不需要死记,面板里能打开配置文件编辑器就对了。
2.3 修改前要做的环境检查
动手前先做好三件事:第一,确认你的OpenSSL版本,宝塔里可以在终端执行openssl version,版本低的话很多新套件根本不支持;第二,检查当前站点SSL配置,在面板“网站-设置-SSL”里截个图或复制一份原配置,用来回滚;第三,确认证书类型,是单域名证书还是泛域名证书,里面包含RSA公钥还是ECC公钥,这决定了认证算法部分是否必须保留RSA。
注意:如果你使用的是ECC证书,配置里保留ECDSA套件即可,性能更好;如果证书是RSA类型,那就保留ECDHE-RSA套件。千万别把认证算法和密钥交换算法搞混,ECDHE-RSA指的是“用RSA证书做身份认证”,但仍然属于安全的密钥交换方式,是可以保留的。
3. 实操:在宝塔面板中禁用RSA密钥交换套件
理论讲完了,现在进入大家最关心的实操阶段。以下步骤基于宝塔面板的常见版本,但配置逻辑在Nginx和Apache上是通用的,我会把两种环境都写清楚,你按需对照即可。
3.1 找出当前生效的加密套件列表
在动手改配置之前,先摸清现状。登录宝塔后台,打开“终端”,执行OpenSSL命令查看当前站点实际支持的套件。注意,直接执行openssl ciphers -v只会显示OpenSSL默认支持的套件,不等于你的站点真的启用了这些。更准确的办法是远程连接验证,比如在本地终端执行:
openssl s_client -connect yourdomain.com:443 -tls1_2 </dev/null 2>/dev/null | grep "Cipher is"这个命令只能看单次握手的选中套件,不够全面。更推荐使用一些在线扫描工具或本地脚本做全量枚举,它们会展示全部套件和协议支持情况。用这种方式先留一个“修改前快照”,方便后面比对。
3.2 Nginx环境:修改站点配置文件
在宝塔面板左侧找到“网站”,点击目标站点右侧的“设置”,进入“配置文件”选项卡。找到server块内部和SSL相关的段落,通常在listen 443 ssl;这一行的下面,会有一行ssl_ciphers配置,或者压根没有——没有的话就自己加上。
把ssl_ciphers这一行替换成下面这个推荐配置:
ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers on;如果原来已经有ssl_ciphers,直接整体替换;如果没有,就按照上面的格式新增。ssl_prefer_server_ciphers on建议显式加上,这样服务器会优先选择列表中靠前的套件,而不是接受客户端可能的低安全偏好。
3.3 Apache环境:修改SSL虚拟主机配置
Apache环境略微不同。在宝塔中进入网站设置后,同样打开“配置文件”,找到VirtualHost *:443段落,里面会有SSLCipherSuite和SSLProtocol配置。如果没有,就在SSL证书配置的下方加上:
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 SSLHonorCipherOrder On注意SSLProtocol那行有两个语义要点:all代表“启用所有协议,但随后减掉后面指定的”,减掉SSLv3、TLSv1、TLSv1.1后,就只剩TLSv1.2和TLSv1.3。这套写法比直接写TLSv1.2 TLSv1.3更保险,因为OpenSSL版本不同,直接枚举协议名的写法偶尔会产生歧义。
3.4 保存并重载服务
配置改完不能直接关页面走人。Nginx环境建议先在终端执行nginx -t检查语法,确认无误后执行nginx -s reload或者service nginx reload生效。Apache环境则执行apachectl -t或httpd -t验证语法,再执行service httpd reload。
重载前务必确认语法检查通过。我曾经见过有人直接在面板里点“保存”但没留意报错弹窗,结果配置没生效,自己还以为是改了没用。记住,面板的“保存”只是写文件,重载才是让Nginx重新读取配置的关键步骤。
提示:宝塔面板支持在“网站-设置-配置文件”页面直接修改,修改完会提示是否需要重载。建议先手动复制一份原配置到本地,再修改。改坏了大不了粘回去,总比急急忙忙去翻备份强。
4. 验证效果:确认RSA密钥交换套件已经失效
配置重载完成不等于事情结束了。很多人栽在这一步,明明改完了,扫描工具还是报漏洞。原因可能是配置没生效、修改错位置、或者在面板其他环节又被覆盖。下面把验证方法写全。
4.1 使用OpenSSL命令快速验证
最快捷的方式是用OpenSSL强制指定RSA密钥交换套件发起握手,如果服务器端确实禁掉了,握手会直接失败。在本地终端执行:
openssl s_client -connect yourdomain.com:443 -tls1_2 -cipher 'RSA' </dev/null 2>&1 | grep -E "Cipher is|handshake failure"正常情况下会出现handshake failure之类的错误,说明RSA类套件已不可用。如果仍然输出Cipher is AES128-SHA之类的信息,说明配置没有生效。再换一组现代套件试试,比如ECDHE-RSA-AES128-GCM-SHA256,这次应该能握手成功:
openssl s_client -connect yourdomain.com:443 -tls1_2 -cipher 'ECDHE-RSA-AES128-GCM-SHA256' </dev/null 2>&1 | grep "Cipher is"4.2 全量扫描确认漏洞已关闭
本地命令只能验证单点,要想看完整的套件支持矩阵,推荐用在线扫描服务或本地枚举工具。以某常用在线平台为例,提交域名后大约几分钟出报告,重点看这几个指标:协议版本是否只保留TLSv1.2和TLSv1.3;套件列表里是否还有任何TLS_RSA开头或TLS_RSA_WITH开头的项目;整体评分是否从B/C提升到A级。
如果扫描结果仍显示RSA密钥交换套件存在,用排除法倒查:检查修改的是不是当前生效的站点配置文件;检查是否改了Nginx主配置但没同步到站点配置;检查是否有CDN或反代在中间层挡住了源站;最后再确认重载动作确实执行成功,没有报错。
4.3 客户端兼容性验证
安全等级提升后,最怕的是老客户端访问不了。这类问题不会在你自己的电脑上暴露,因为Chrome、Edge、Firefox都很新,安全基线比你的服务器还高。真正需要测试的是旧手机的浏览器、微信内置浏览器、部分政府单位或企业内部老系统里的IE核心浏览器。
我在一次兼容性测试中遇到过这样的场景:一个运营系统在Windows 7的老机器上通过IE11访问站点,关闭TLSv1后页面直接白屏。排查确认,这个老环境只支持TLSv1.0和部分旧套件。这种情况下,技术方案和业务需求产生了冲突,安全部门要求关,业务部门要求能开。最终的处理方式是在Nginx里按路径或域名拆分配置,核心业务站点保持新配置,给特定老设备访问的页面单独加一条TLSv1白名单入口。现实中的安全工作经常不是非黑即白,但至少在你自己能控制的部分,不要把低安全基线作为默认值。
5. 常见问题与踩坑记录
这部分是整篇文章含金量最高的地方。网上到处是配置教程,但真正踩过坑才知道哪些环节最容易被忽视。我结合自己的实操经验和帮别人排查的案例,把常见问题一条条说透。
5.1 改完打不开网站,全站502或SSL握手失败
最常见的反面教材是ssl_ciphers字符串里混入了OpenSSL版本不支持的套件名称。比如在老版本OpenSSL 1.0.2上,CHACHA20系列套件可能不存在,配置一加载直接报错。检查方法是看Nginx错误日志,通常在/www/wwwlogs/error.log里,会明确写出unknown directive或者error loading certificate之类的信息。
另一个同时踩中的坑是冒号分隔符写成了逗号,Nginx对这类错误还算友好,会直接报错拒绝启动,Apache则会默默忽略部分内容,导致实际生效的配置和你期望的不一致。所以每次改完配置,必须用nginx -t或apachectl -t做语法检查,这条规矩在哪个环境都不能破。
5.2 扫描显示RSA套件还在,但配置里明明已经删掉了
这种诡异情况通常原因有三。其一,你改的是某个站点的配置文件,但该站点实际使用的是Nginx主配置里的默认SSL段,两个地方都定义了ssl_ciphers时,具体生效位置取决于嵌套层级,主配置的某些指令会被站点级配置覆盖,也可能反过来,排查时两边都要看。其二,宝塔面板开启了“防御”模块或某些商业插件,它们会注入自己的SSL配置,覆盖站点配置。其三,你扫的是做了CDN加速的域名,回源和边缘节点各自有独立证书和独立套件,你修的源站套件跟外部扫描看到的完全是两码事。
遇到这种情况,最有效的方法是分链路验证:先在服务器本机执行openssl s_client -connect 127.0.0.1:443 -servername yourdomain.com来测源站,再用外部扫描测域名。两个结果一对比,就知道问题出在哪一层了。
5.3 只关了TLSv1.0和TLSv1.1还不够
把协议版本调高到TLSv1.2以上,确实能堵住一部分老漏洞,因为RSA密钥交换套件大多绑在旧协议上。但TLSv1.2协议本身也支持RSA密钥交换套件,比如TLS_RSA_WITH_AES_128_CBC_SHA就允许在TLSv1.2下运行。所以“关闭TLSv1.0/1.1”和“禁用RSA密钥交换”是两个独立操作,后者必须通过限制ssl_ciphers来实现。扫描工具里如果这两项都标记了,就要同时处理协议和套件两处配置。
我在做配置检查时习惯用一个笨办法,把协议和套件各写一行注释,逐行核对,避免改完协议忘了套件,或者反过来。
5.4 HTTP/2与OCSP Stapling的连带问题
升级套件之后,有些站点会突然发现HTTP/2失效,Chrome开发者工具里Network面板显示协议从h2变成了http/1.1。常见原因是HTTP/2实现本身对套件有要求,特别是主流浏览器的HTTP/2黑名单机制,会禁用一批老套件。你重新配置套件后,如果选定的是Chrome黑名单之外的套件,问题一般会自动消失。如果还不行,检查是否启用了TLSv1.3但OpenSSL版本过低,Nginx的HTTP/2模块要配合特定版本才稳定,宝塔面板一般会自动处理好,但也要留意版本提示。
OCSP Stapling的坑不太明显:开启后Nginx会定期向证书颁发机构查询证书吊销状态并缓存。如果证书链不完整,或者网络环境到CA的通路不稳,就可能出现握手变慢甚至偶发失败。改完套件后顺手把OCSP也测一遍,能省心的尽量省心。
5.5 前后端分离场景下的“套件继承”问题
现在很多站点用Nginx做反向代理,后端还有Java或Node服务。如果你只改了前端的Nginx套件,扫描工具还是可能穿透到后端暴露的端口。比如8080、8443这些端口如果没有防火墙限制,外部直接访问后端服务时,照样能协商出RSA套件。这种情况在服务器上跑多个Java应用服务时特别常见。
安全加固要记住木桶原理,出口上再做一道“只允许本地访问”。如果后端服务必须对公网开放,那就在后端服务的SSL配置里同样应用这套套件策略,或者用Nginx做统一入口后通过防火墙封掉后端公网端口。排查的时候不要只看80和443,全端口扫描一遍是基本操作。
5.6 离线安装环境最容易忽略的库依赖
还有一种情况现在越来越多,服务器在纯内网或离线环境部署,宝塔面板的软件安装可能用了本地源或离线包。这种环境下OpenSSL可能是基础系统自带的老版本,功能不全,或者Nginx编译的时候本来支持很多协议,但运行时链接的是另一个OpenSSL库。
我曾经在某离线项目的服务器上遇到过,配置了TLSv1.3但握手始终只显示TLSv1.2,折腾了半天才发现系统里有两个OpenSSL,Nginx链接的是老版本。排查方式是检查nginx -V输出,确认内置的OpenSSL版本,也可以在面板“软件商店”里看Nginx的当前版本和依赖信息。这个问题不常见,但一旦遇到,比配置本身难搞得多。
结尾
折腾完这一圈,我个人最大的体会是:安全加固这种事,看起来只是复制一段配置,真正拉开差距的是排查链路和兼容性思维。单机自己玩怎么改都行,上了生产、接了业务、还要面对各种扫描验收的时候,你就会发现“改配置”反而是整件事中最轻松的一部分。我自己的习惯是每台服务器建一个ssl-config-notes文档,记录每次修改的时间、改动内容、验证结果和当时的OpenSSL版本,这样即使过了三四个月站点突然出问题,也能快速定位是不是某次加固改动埋的雷。
最后再分享一个小建议:改完RSA套件之后不要马上把旧配置从笔记里删除,保留至少一个版本周期。因为很多老业务系统的运维根本不知道自己的客户端还依赖旧协议,一旦雪崩式反馈“网站打不开”,有回滚方案就能在五分钟内恢复业务,比临时翻文档、翻备份快得多。安全是目标,可用是底线,两者平衡好了,这套配置才算真正落地。