浏览器
SSL_ERROR_HANDSHAKE_FAILURE_ALERT:Firefox 握手失败怎么办
SSL_ERROR_HANDSHAKE_FAILURE_ALERTSSL_ERROR_HANDSHAKE_FAILURE_ALERT 的意思是:服务器用一条 handshake_failure 警报结束了 TLS 握手。 Firefox 显示“建立安全连接失败”,正文是“连接到 example.com 时发生错误。SSL 对等端无法协商出一个可接受的安全参数设置。”服务器不说具体原因,最常见的两个是:服务器要求客户端证书而 Firefox 没有发送;或者服务器只接受 Firefox 已经不再提供的老旧加密套件。
这个警报是什么意思
这是服务器发出的 40 号警报,表示它无法和 Firefox 协商出可接受的安全参数。TLS 服务器遇到致命问题时会发送带编号的警报,TLS 1.3 现行规范 RFC 9846 对 handshake_failure(40)的定义是:发送方在可选范围内“无法协商出一组可接受的安全参数”。Firefox 使用的 TLS 库 NSS 把这个警报映射为 SSL_ERROR_HANDSHAKE_FAILURE_ALERT。
| Firefox 错误代码 | 谁发现的问题 |
|---|---|
SSL_ERROR_HANDSHAKE_FAILURE_ALERT |
服务器,发送了 handshake_failure |
| SSL_ERROR_NO_CYPHER_OVERLAP | Firefox 自己,找不到共同套件 |
SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT |
服务器,发送了 TLS 1.3 的 certificate_required |
| SSL_ERROR_UNSUPPORTED_VERSION | 协议版本不匹配 |
同样的服务器行为在 Chrome 里通常显示为 ERR_SSL_VERSION_OR_CIPHER_MISMATCH,涉及客户端证书时显示为 ERR_BAD_SSL_CLIENT_AUTH_CERT。
先判断是不是只有你打不开
换浏览器、换网络、从外部检测,这三步能定位原因:哪一步之后能打开,原因就在哪一步。
- 换个浏览器。 Chrome、Edge 能打开而 Firefox 不行,多半是客户端证书或 Firefox 自身设置的问题,看下面第 1、3 条。
- 换个网络。 用手机热点能打开、公司网络不行,说明中间的代理或防火墙在干扰,看第 4 条。
- 从外部检测网站。 用 SSL 检测 测一下,外部也握手失败,就是服务器配置问题,只能联系网站管理员。
这个页面没有“接受风险并继续”按钮:握手没有完成,不存在可以继续使用的连接。
常见原因(按出现频率排序)
这个警报有 5 种常见原因,最常见的是服务器要求客户端证书而 Firefox 没有发送。
1. 服务器要求客户端证书
企业门户、政务系统、银行后台、VPN 网关经常要求双向 TLS。Firefox 没发证书,强制要求证书的 OpenSSL 系服务器就会中止握手。OpenSSL 在 TLS 1.2 连接上会把 TLS 1.3 的 certificate_required 警报换成 handshake_failure,所以走 TLS 1.2 时看到的恰好就是这个错误。
确认服务器是否在要客户端证书:
openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null 2>/dev/null \
| sed -n '/Acceptable client certificate CA names/,/Requested Signature/p'
输出了一串 CA 名称,说明服务器在请求客户端证书。Apache 用 SSLVerifyClient require 就是这种效果;Nginx 的 ssl_verify_client on; 则会先完成握手、再返回 HTTP 400,不会出现这个错误。
Firefox 这边:90 及以后的版本会自动到 Windows 和 macOS 的系统证书库里找客户端证书;放在智能卡或 USB Key 里的证书,需要在“设置 > 隐私与安全 > 安全设备”里加载厂商提供的 PKCS#11 模块;过期的个人证书等同于没有证书。
国内还有一种情况:不少网银、政务、办公系统靠 USB Key 做客户端认证,配套的控件和驱动往往只适配 IE 内核。360 官方的说明里也写到,双核浏览器的 IE 内核主要就是为这类网站保留的。遇到这种系统,按网站要求使用指定浏览器或兼容模式,比折腾 Firefox 更省事。
2. 服务器只接受老旧的加密套件
老设备、嵌入式管理界面、长期没更新的服务器,可能只接受现在 Firefox 已经不再提供的套件。看看服务器接受什么:
openssl s_client -connect example.com:443 -servername example.com -tls1_2 -brief </dev/null
nmap --script ssl-enum-ciphers -p 443 example.com
如果只列出老的 CBC 或 3DES 套件,就必须升级服务器。具体套件可以用 加密套件查询 查。
3. Firefox 的 TLS 设置被改过
一些加固教程和扩展会修改 about:config 里的 security.ssl3.* 或 security.tls.version.*。如果关掉了服务器需要的套件,服务器就会回这个警报。新建一个干净的配置文件测试(地址栏输入 about:profiles,新建配置文件);新配置文件里正常,就把改过的首选项恢复默认。
4. 中间有安全软件或代理
杀毒软件的 HTTPS 扫描和公司代理会自己终止 TLS。如果它们对外连接用的配置过时,上游服务器会拒绝它们,警报再被转给 Firefox。临时关闭 HTTPS 扫描或换个网络就能判断,详见 杀毒软件 HTTPS 扫描。
5. TLS 终止设备上没配这个域名
有些负载均衡和 CDN 在没有对应域名证书时,不发 unrecognized_name,而是直接发一个笼统的失败警报。把上面的 openssl s_client 命令分别带和不带 -servername 跑一次对比。
怎么修
用户这边要让 Firefox 能找到并发送个人证书;服务器这边要开启 TLS 1.2/1.3 和现代套件,并给每个域名配好证书。
需要个人证书的网站用户:
1. 把 Firefox 升级到最新版本。
2. 把证书装进系统证书库,或者在“设置 > 隐私与安全 > 证书 > 查看证书 > 您的证书”里导入 .p12/.pfx 文件。
3. 智能卡、USB Key 用户,在“安全设备”里加载厂商的 PKCS#11 模块。
4. 刷新页面,Firefox 弹窗时选择对应证书。
服务器管理员:
- 开启 TLS 1.2 和 1.3,使用 ECDHE 加 AES-GCM 或 CHACHA20 的套件;TLS 配置生成器 可以直接生成可用配置。
- 如果只有部分用户需要客户端证书,Apache 用 SSLVerifyClient optional、Nginx 用 ssl_verify_client optional;,在应用里处理没带证书的情况,用户就能看到一个可读的提示页,而不是握手直接失败。
- 每台 TLS 终止设备上都要给每个域名配好证书。
怎么确认已经修好
openssl 能完成握手、Firefox 重新打开不再报错,就说明修好了。
openssl s_client -connect example.com:443 -servername example.com -brief能完成握手,打印出协议和套件。- 双向 TLS 的,带上客户端证书测试:
openssl s_client -connect example.com:443 -servername example.com -cert client.pem -key client.key -brief。 - 用 Firefox 重新打开网站;SSL 检测 可以从外部确认服务器一侧是否正常。
常见问题
这是我电脑的问题还是网站的问题?
多数是网站的问题,或者是网站对你有额外要求。服务器拒绝了这次握手:如果网站需要个人证书(U 盾、智能卡、单位发的证书),要在你这边装好并选中证书;否则就是服务器的 TLS 配置需要更新。
Chrome 能打开,Firefox 打不开,为什么?
Windows 上的 Chrome、Edge 直接用系统证书库里的客户端证书。旧版 Firefox,或者证书存放在 USB Key 里、但没加载厂商 PKCS#11 模块的 Firefox,看不到这张证书,服务器就会拒绝握手。升级 Firefox,并在“设置 > 隐私与安全 > 安全设备”里加载 Key 的模块。
要不要去 about:config 里改 security.tls 设置?
只在“撤销以前的修改”时才改。如果之前按某些加固教程禁用过加密套件或提高过最低 TLS 版本,把这些首选项重置为默认值即可。为了访问一台老服务器去打开过时的选项,会降低所有网站的安全性。
和 SSL_ERROR_NO_CYPHER_OVERLAP 有什么区别?
SSL_ERROR_NO_CYPHER_OVERLAP 是 Firefox 自己发现双方没有共同套件;SSL_ERROR_HANDSHAKE_FAILURE_ALERT 是服务器判断无法继续、用警报通知了 Firefox,具体原因被服务器藏起来了。