HTTP/2 和 HTTP/3 抓包:为什么 Follow TCP Stream 不好使了

以前抓 HTTP,右键 → Follow → TCP Stream,请求响应整整齐齐排好,一眼看完。

现在同样的操作,屏幕上是一堆交错的方块,一会儿是 HEADERS,一会儿是 DATA,Stream ID 跳来跳去。想看某个接口的响应,得在一坨数据里找。

不是 Wireshark 变难用了,是 HTTP 变了。这篇文章讲清楚变了什么,以及在新协议下该怎么抓。

HTTP/2 到底改了什么

一句话:一条 TCP 连接上跑很多条并行的流。

HTTP/1.1 的做法是一条连接一次一个请求,浏览器开六条连接凑并行。HTTP/2 把所有请求压进同一条 TCP,每条流有个编号(Stream ID),帧头里带着这个号,接收方按号拼回各自的请求。

好处是省连接、省握手、头部压缩(HPACK)。坏处是——对抓包的人——数据在物理上就是交织的:

Stream 1 HEADERS
Stream 3 HEADERS
Stream 1 DATA
Stream 5 HEADERS
Stream 3 DATA
Stream 1 DATA (END_STREAM)

Follow TCP Stream 是按字节顺序拼的,它不知道哪几个字节属于哪条流,所以给你看到的就是上面这种交错的样子。原始数据没错,只是没有语义。

先解决”看不到”的问题

打开抓包文件,如果只有 TLS Application Data,说明没解密,后面全都不用谈。

浏览器的话,设 SSLKEYLOGFILE 环境变量再启动,然后把 Wireshark 的 TLS 首选项指向那个文件。具体步骤上一篇 Wireshark 入门里写了,这里不重复。

解密成功后,协议列会从 TLS 变成 HTTP2,同时在 Info 里能看到 HEADERS[1]、DATA[1] 这样的标注。

还有一个前提容易被忽略:必须走 TLS 且 ALPN 协商成 h2。现在几乎所有网站都是这样,但如果你抓的是内网某个老服务,它可能还在跑 HTTP/1.1,那就还是老样子。看 TLS 握手里的 ALPN 字段,能确认协商的是什么。

纯明文的 h2c(不带 TLS 的 HTTP/2)理论上存在,实际极少,遇到了 Wireshark 需要手动指定「Decode As → HTTP2」。

三个过滤器,让 h2 变得可读

http2.streamid == 3

只看 3 号流。这是最实用的一个——先在概览里找到目标请求的 Stream ID,然后锁定它,世界立刻清净。

http2.headers

只看头部帧,跳过 DATA。想看接口路径和状态码的时候用这个。

http2.header.name contains "content-type"

按头部名过滤。查某个字段(比如 set-cookie)什么时候出现,比人工翻快得多。

想一次性把某条流的请求头导出来:

tshark -r cap.pcapng -Y "http2.streamid==3 && http2.headers" \
  -T fields -e http2.header.name -e http2.header.value

输出是每行一个头,直接能粘到表格里对比。

新版有个 Follow HTTP/2 Stream

Wireshark 近几年的版本里,右键菜单多了一项 Follow → HTTP/2 Stream,能把单条流的数据拼出来。找到它之后,h2 的体验就接近以前了。

但它和 TCP Stream 有个同样的坑:中文默认按 Latin-1 显示。右下角切 UTF-8。

如果没有这一项,说明版本太老,升级比折腾过滤器划算。

HTTP/3 换了传输层

HTTP/3 不在 TCP 上跑了,它跑在 QUIC 上,QUIC 跑在 UDP 443。

这个变化带来的第一个后果很直接:所有基于 TCP 的分析手段全部失效。没有三次握手,没有 Follow TCP Stream,tcpdump 的 tcp port 443 一条都抓不到。

抓包过滤要改成:

udp port 443

就这一条。但抓下来之后,如果没解密,你看到的是 QUIC 的加密载荷,INFO 里只有包类型(Initial、Handshake、1-RTT),没有内容。

QUIC 把 TLS 1.3 内置了,所以解密同样需要密钥日志。好消息是 Wireshark 的 TLS 首选项里那个 keylog 文件对 QUIC 也生效,配一次两边都能解。

解密后协议列显示 HTTP3,能看到 HEADERS 和 DATA 帧,过滤用:

http3.headers
http3.streamid == 0

三个”为什么我抓不到 HTTP/3″

一、网站根本没启用。HTTP/3 需要考虑 UDP 被中间设备拦掉的情况,很多站点只对部分用户开,或者干脆没开。判断方法:看响应头有没有 alt-svc: h3=":443"。没有就是没开。

二、浏览器回退了。Chrome 会先试着用 QUIC,失败就退回 TCP。如果网络环境里 UDP 443 被封(公司网络很常见),你只会看到 h2 的流量,而且是”看起来正常”的那种。

三、Wireshark 版本太老。QUIC 的草案改过很多次,老版本可能把包识别成 UDP 或者解错。这类问题只能升级解决。

一个不依赖解密的办法

如果你只是想知道”这个页面到底发了多少个请求、各自耗时多少”,根本不需要抓包。

Chrome 打开 chrome://net-export/,开始记录,复现问题,停止,得到一个 JSON。再用 netlog-viewer 打开,能看到每条请求用的协议(h2 还是 h3)、连接复用情况、每个阶段的耗时。

这个方法的优势是:不用解密、不用配密钥、不受加密影响。缺点是看不到原始字节,只能看事件。

我一般的顺序是:先 net-export 看宏观(哪个请求慢、走的什么协议),再决定要不要抓包看细节。反过来做,经常抓了一堆包才发现问题根本不在网络上。

什么时候该用哪个

想知道”发了什么” —— 抓包 + 解密。h2 用 streamid 锁定,h3 用 http3.streamid。

想知道”慢在哪” —— 先 net-export,再决定。很多”网络慢”其实是服务端 TTFB 慢,抓包只能看到等待,看不到原因。

想验证”服务端有没有开 h3″ —— 一条 curl -I --http3 https://目标,看响应头。比自己抓省事。

最后

协议在变,工具在跟。HTTP/2 让”按字节顺序看”这个直觉失效,HTTP/3 干脆把传输层换了。

但底层逻辑没变:先确定流量走在哪条链路上、有没有加密、能不能解密。这三件事没搞清楚,换什么工具都是白抓。

顺带说一句,h2 的多路复用对排查也有好处:所有请求挤在一条连接上,你不用在六条连接里找对应关系了。前提是你得会按 streamid 过滤。


抓包分析仅限自有服务与授权范围。解密他人 HTTPS 流量,即使技术上可行,也属于违法行为。

© 版权声明
THE END
喜欢就支持一下吧
点赞15赞赏 分享
相关推荐
评论 抢沙发

请登录后发表评论

    暂无评论内容