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

相关推荐
Mr Xu_9 分钟前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝13 分钟前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions21 分钟前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发21 分钟前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
2501_9151063223 分钟前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview
程序员猫哥_29 分钟前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞0529 分钟前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、35 分钟前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao35 分钟前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架
杨超越luckly41 分钟前
HTML应用指南:利用GET请求获取中国500强企业名单,揭秘企业增长、分化与转型的新常态
前端·数据库·html·可视化·中国500强