SSL密钥协商导致抓包失败的原因分析

前几天逛L站的时候,看到有网友发了一个咨询帖子,说他有一个个人网站,想用Reqable抓包分析一下流量,但是开启代理后浏览器无法访问,关闭后可以正常访问,不确定是哪里的原因。我排查定位了下,这里给大家做个简单的分享。

1. 问题复现

问题还是很容易复现的,打开Reqable启用系统代理并抓包,使用Chrome浏览器访问网站,看到浏览器报错ERR_CONNECTION_CLOSED,Reqable中能看到此域名的流量,但是状态全是黄色警告,点开一条记录提示是服务器SSL握手失败。

我们经常会遇到的是客户端SSL握手失败,往往是客户端不信任中间人签发的证书,中断请求。这里的服务器SSL握手失败,表明是服务器拒绝了握手,导致连接失败。

服务器拒绝SSL连接的原因下面这些:

  • 启用了双向验证。需要客户端内置证书才能访问,浏览器这里绝无可能,我们可以排除掉。
  • 启用了SSL指纹校验。一般为了防抓包,服务器会校验客户端请求的指纹是否和预期一致。这位网友说只是Nginx部署了一个静态网页项目,没有这种可能。
  • SSL/TLS版本不一致的问题。比如服务器强制使用了TLS 1.3,用到一些比较新的加密套件算法,密钥协商算法等。Reqable可能由于版本过低,或者算法不支持导致服务器SSL握手失败。

Reqable使用的是OpenSSL作为SSL支持,编译的是1.1.1n版本,OpenSSL现在主要是支持3.x版本,已经更新到3.6了。

我怀疑问题可能出现在这里,下面来验证一下。

2. 原因排查

我看了下电脑安装的OpenSSL版本恰好是最新的3.6版本。

yaml 复制代码
openssl -vOpenSSL 3.6.0 1 Oct 2025 (Library: OpenSSL 3.6.0 1 Oct 2025)

下面用OpenSSL命令来测试下这个网站的域名。

bash 复制代码
openssl s_client -connect xxx.top:443

控制台输出能看到SSL正常连接,没什么问题:

vbnet 复制代码
Connecting to xxx.xxx.xxx.xxx
CONNECTED(00000005)
depth=2 C=US, O=Internet Security Research Group, CN=ISRG Root X1
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=R12
verify return:1
depth=0 CN=xxx.top
verify return:1

下面我们开始搬出大杀器Wireshark,来分析具体的SSL/TLS层协议,看看Reqable和浏览器哪里不同。

主要是看ClientHello包,里面包含了客户端的SSL各种信息。上图左边是浏览器的数据包,右边是Reqable的数据包。重点观察的部分,我已经用红框标出来了,主要有三个部分。

  • Cipher Suites,加密套件。如果服务器指定了某个加密套件,但是Reqable支持的加密套件列表里面不包含,就可能出现SSL握手失败的情况。从浏览器访问回复的ServerHello包来看,选择的是加密套件是Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301),已经在Reqable支持的列表中,故而可以排除掉。
  • Extension: signature_algorithms,支持算法列表。Reqable提供了19种算法,浏览器提供了8种算法,仔细对比发现,Reqable并没有缺少的部分。
  • Extension: key_share 密钥协商算法(也被称为密钥协商)。这是TLS 1.3新增的重要扩展,用于减少握手步骤和增加前向安全。浏览器支持X25519MLKEM768和X25519,Reqable只支持X25519。

我们怀疑是密钥协商算法不支持X25519MLKEM768导致的,这个算法从OpenSSL 3.5版本才开始支持的。为了验证这个问题,我们可以在OpenSSL的命令中使用-curves来指定。

arduino 复制代码
openssl s_client -connect xxx.top:443 -curves X25519

我们发现SSL握手失败了。

bash 复制代码
Connecting to xxx.xxx.xxx.xxx
CONNECTED(00000005)
4061CE0C02000000:error:0A000410:SSL routines:ssl3_read_bytes:ssl/tls alert handshake failure:ssl/record/rec_layer_s3.c:916:SSL alert number 40

重新用-curves来指定为X25519MLKEM768:X25519则可以正常握手(注意中间用冒号隔开)。

arduino 复制代码
openssl s_client -connect xxx.top:443 -curves X25519MLKEM768:X25519

3. 修复方案

我们接下来考虑如何在Reqable中修复这个问题,首先需要更新OpenSSL版本到3.5以上。这个比较麻烦,由于我们C++库使用CMake构建并编译,OpenSSL并不支持CMake,更新比较麻烦。不过有个暂时取巧的办法,编译时直接链接到本机已安装的OpenSSL库。

作为临时测试,我们修改代码,强行设置密钥交换算法为X25519MLKEM768:X25519。如果要完整处理,我们需要还原客户端的密码交换算法。

c++ 复制代码
SSL_set1_groups_list(ssl, "X25519MLKEM768:X25519");

重新编译,再次运行Reqable开始抓包,看起来没有问题了。

4. 更新计划

目前的3.0版本我们还没有修复这个问题,计划在后面3.1版本处理。3.0后续还有几个小版本,预计在12月之内完成,明年正式开始3.1版本的开发。

3.1版本主要会优化抓包部分的功能和实现,包括集成最新OpenSSL库、还原SSL指纹、增强模式优化和重写依赖原始服务器响应等问题。

Reqable,最强API抓包和测试工具,欢迎访问我们的官网:reqable.com

相关推荐
Kratzdisteln1 小时前
【TIDE DIARY 5】cursor; web; api-key; log
前端
Danny_FD1 小时前
使用docx库实现文档导出
前端·javascript
良木林1 小时前
webpack:快速搭建环境
前端·webpack·node.js
网络点点滴1 小时前
Vue3路由的props
前端·javascript·vue.js
last demo1 小时前
grep和sed
linux·运维·前端·chrome
-曾牛1 小时前
深入解析 XSS 漏洞:原理、分类与攻防实战
前端·安全·web安全·网络安全·渗透测试·xss·原理解析
JK凯1 小时前
前端调试技巧
前端·visual studio code·前端工程化
开源之眼1 小时前
github star 基础IO 文件在内核中是怎么被管理的 重定向的含义 在自定义shell中加入重定向
前端
JZXStudio1 小时前
独立开发者亲测:MLX框架让我的App秒变AI原生!15年iOS老兵的2025新感悟
前端·ios