当前主要可以通过浏览器和Wireshark等工具调试HTTP/2流量。
使用浏览器调试HTTP/2流量
HTTP/2 引入二进制分帧层(Binary Framing),将每个请求和响应分割为更小的帧,并对它们进行二进制编码。与此同时,HTTP/2 沿用之前 HTTP/1.1中的绝大部分语义,上层应用基本上感知不到 HTTP/2 的存在。通过浏览器提供的网络调试工具我们可以清晰地看到请求和响应的详细信息。
对于Chromium浏览器,打开”更多工具(L) -> 开发者工具(D)”,然后选中”Network”,并选中要查看的文件,就可以清晰地看到HTTP/2请求及响应的信息了。如:
从上图可以看到,HTTP/2资源的内容,与HTTP/1.1资源的内容相比,只有一些细微的变化,如所有的头部字段名都是小写的,引入了以”:”开头的伪首部字段等。
然而,浏览器的调试工具只能看到请求和响应的信息。对于连接建立、数据传输,及流控这样的过程信息,浏览器的调试工具就显得无能为力了。这些更细节的信息可以通过Wireshark来看。
使用 Wireshark 调试 HTTP/2 流量
尽管HTTP/2规范并没有严格要求只能基于TLS传输HTTP/2流量,然而目前基于TLS传输HTTP/2已然成为事实的标准了。绝大部分提供HTTP/2服务器的网站都基于TLS来提供,常见的客户端浏览器,如Chrome,Firefox更是完全不提供对明文传输的HTTP/2的支持。因而通过Wireshark调试HTTP/2流量最主要的即是解密HTTPS流量,进而借助Wireshark对HTTP/2数据的解析,看到客户端与服务器之间详细的帧交互过程。
Wireshark 的抓包原理是直接读取并分析网卡数据,要想让它解密 HTTPS 流量,有两个办法:1)如果拥有 HTTPS 网站的加密私钥,可以用来解密这个网站的加密流量;2)某些浏览器支持将 TLS 会话中使用的对称加密密钥保存在外部文件中,可供 Wireshark 解密之用。
设置SSLKEYLOGFILE
环境变量解密HTTPS流量
Firefox 和 Chrome 都支持生成上述第二种解密方式所需的文件,具体格式见 NSS Key Log Format。但只有设置了系统环境变量SSLKEYLOGFILE
,Firefox 和 Chrome 才会生成该文件,它们将这个系统变量的值作为文件保存的路径。先来加上这个环境变量(Ubuntu):
接着,通过选择Edit -> Preferences... -> Protocols -> SSL
打开Wireshark 的 SSL 配置面板(Ubunut版,Mac版通过Wireshark -> Preferences...
打开首选项),在「(Pre)-Master-Secret log filename」选项中选择SSLKEYLOGFILE
文件,或输入该文件的路径。如下图:
最好将「SSL debug file」也配上,这样解密过程中的日志都会记录下来,便于调试分析。
通过 终端 启动 Firefox 或 Chrome(确保这些浏览器能读取到环境变量):
这时再访问 HTTPS 网站,sslkeylog.log
文件中应该包含浏览器写入的数据。如下面这样:
检查无误后,就可以开启 Wireshark,选择合适的网卡开始抓包。为了减少不必要的数据包对我们分析的干扰,我们可以只针对某个域名的TCP 443端口来抓包,以减少抓取的数据包的数量,如:
新版 Wireshark (如我目前在用的Version 2.0.2) 在配置了 TLS 解密后,会自动识别并解析 HTTP/2 流量。访问想要抓包的 HTTP/2 网站,就可以轻松看到想要的 HTTP/2 数据包了。如下图:
这种方法也可以用在解密使用 HTTP/1 的 HTTPS 网站上。
每次都要从终端启动浏览器还是挺麻烦的,我们也可以为GUI设置环境变量,以便于我们不在命令行终端启动浏览器也可以解密HTTPS流量。方法是:
也可以通过其它任何个人偏好的编辑器为/etc/profile
添加export
那一行。更新了/etc/profile
之后,注销当前登录并重新登录。在GUI中启动chromium浏览器,也可以通过Wireshark解密HTTPS流量了。不过要注意,这个设置对所有用户均全局生效。为了系统安全最好在利用Wireshark调试HTTPS流量之后移除这一设置。
Wireshark就像一副眼镜一样,戴上它之后,可以让我们对网络世界发生的一切看得更加清楚,然而即使有了这样强大好用的工具,要想对协议有真正的理解,仔细地阅读协议规范文件也是必不可少的,关于HTTP/2协议的细节,可以参考HTTP2规范(RFC7540)、HPACK:HTTP/2的首部压缩 (RFC7541)和应用层协议协商(ALPN)规范(RFC7301)。
导入服务器私钥解密HTTPS流量
还可以通过为Wireshark导入服务器RSA私钥来解密HTTPS流量。方法是选择Edit -> Preferences... -> Protocols -> SSL
打开Wireshark 的 SSL 配置面板,点击”RSA keys list”后的”Edit…”按钮:
添加一个RSA私钥项,输入适当的IP地址,端口,解密后的协议,及私钥文件的路径,其中私钥必须存为 PEM 格式,这种格式的私钥文件类似于下面这样:
对于nginx服务器来说,通常通过ssl_certificate_key
项配置的是站点私钥,这个私钥通常是在申请证书时,通过类似于如下这样的方式产生:
关于私钥格式的更多信息,可以参考Wireshark的SSL Wiki。
但这种方法对客户端与服务器协商的加密套件有要求。如果加密套件的密钥交换算法是ECDHE,也就是当前大多数HTTPS流量所选择的算法,则解密HTTPS流量将失败。可以通过Wireshark抓取的TLS握手的Server Hello
消息来查看客户端与服务器协商的加密套件:
客户端与服务器协商加密套件的过程大体为,客户端在其TLS握手的Client Hello
消息的Cipher Suites
字段中发送自己支持的加密套件。如通过curl访问http2资源:
可以看到curl支持的加密套件集:
对于nginx,我们通过ssl_ciphers
选项为其配置加密套件,如:
加密套件以”:”分隔。服务器从为其配置的加密套件集中选择排序最靠前的客户端支持的加密套件。对于上面的服务器加密套件集配置,用curl访问服务器,协商出来的加密套件为TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
,这种加密套件的密钥交换算法正是ECDHE,也即不支持Wireshark加密的算法。
我们可以通过修改服务器的配置,以便Wireshark可以解密HTTPS流量。修改之后nginx服务器的加密套件配置为:
与前面那个配置相比,仅有的改动是对调了RSA+AES128
和EECDH+AES128
两个加密套件的位置。再次通过curl访问服务器,可以看到HTTP2流量被解密了:
这次协商的加密套件为TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
,其密钥加密算法为RSA。
不过此时通过chrome浏览器访问网站,就会发现页面已经打不开了,如下图所示:
仔细看的话,可以看到ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY
。这是由于为了安全性考虑,HTTP/2规范建立了加密套件黑名单,并强制要求HTTP/2服务不得配置这样的加密套件。Chromium浏览器严格遵守HTTP/2规范,且在TLS协商阶段,服务器选择了加密套件黑名单中的TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
,因而连接直接被断开了。通过Wireshark抓包来看:
可见是HTTP2层,在与服务器建立连接之后,浏览器就立即发送GOAWAY
帧断开了连接。
然而curl似乎并没有严格遵守HTTP/2规范。
是否可以通过为Wireshark添加RSA私钥解密HTTPS流量的决定性因素,在于客户端与服务器协商的加密套件,而不在于通过HTTPS传输的流量是HTTP/1.1的还是HTTP/2的。不过可以通过这种方法让Wireshark解密的所有加密套件都已经进了HTTP/2规范的加密套件黑名单,因而对于符合规范的HTTP/2流量传输,都是无法通过这种方法来解密流量的。
判断HTTP2是否启用
如前面所述,在某些情况下,直接解密HTTP/2流量确实是难以实现的。而如果我们的需求仅仅是判断HTTP/2是否启用的话,则还是可以通过Wireshark实现的。
HTTP/2在TCP连接建立完成之后,TLS握手的过程中,会进行协议协商,以确定客户端和服务器之间通信最终所用的应用层协议。这个协议协商协议成为ALPN。ALPN是TLS的特性,客户端的SSL/TLS库及使用SSL/TLS库的模块,如HTTP栈,只要支持这一扩展,在发送Client Hello消息时,就会带上相关消息,如:
服务器的SSL/TLS库及Web服务器如果能识别这一扩展,会在Server Hello消息中的相同扩展处放上服务器所选择的用户双方通信的协议标识。如果为服务器配置了支持HTTP/2时,Server Hello消息的ALPN扩展值将为h2
,否则将是http/1.1
,如:
因而尽管对于无法解密TLS流的HTTP/2流,在Wireshark中无法查看传输更多的细节过程,但还是可以通过Wireshark抓取到的Server Hello
消息的ALPN扩展值判断协议协商的结果。
参考文档:
SSL - The Wireshark Wiki
Ubuntu系统环境变量详解
使用 Wireshark 调试 HTTP/2 流量
三种解密 HTTPS 流量的方法介绍
为curl命令启用HTTP2支持