0%

HTTPS抓包的原理和实现

实际上生产环境中我们存在大量的业务请求都走的HTTPS,那么如何抓取调用下游的请求成了问题!如果是HTTP还行,你这HTTPS呢,能用tcpdump抓但是我们没有证书的私钥哇也解析不了哇。目前主流的HTTPS抓包工具都是通过MITM (Man-In-The-Middle 中间人攻击)实现的, 具体原理和实现本文也会讲到, 所以这里我们就不造轮子了直接用开源的mitmproxy,如果你是本地抓包的话完全可以用WiresharkCharles 工具!

使用mitmproxy抓取HTTPS流量

注意

  1. 如果你第一次操作建议自己起一个容器进行测试,别直接上来在自己机器上搞!可以安装个Debian/Ubuntu的镜像先试试!
  2. 请勿在线上生产环境使用,如果使用请明确影响面后再使用!

安装 mitmproxy

1
2
3
4
5
# 通过pip3源码安装(推荐,能第一时间安装到最新的)
pip3 install mitmproxy

# 根据Linux发行版的内置版本下载
sudo apt-get install -y mitmproxy

安装完成后可以查看下版本

1
2
3
4
5
~ mitmproxy --version
Mitmproxy: 4.0.4
Python: 3.7.3
OpenSSL: OpenSSL 1.1.1n 15 Mar 2022
Platform: Linux-5.4.143.bsk.8-amd64-x86_64-with-debian-10.12

注意: 有些服务器的Linux发行版本比较低,实际上 mitmproxy 就处于不可用的状态,此时可以使用我自己开发的一个 https 抓包工具 devtool!

启动 mitmproxy

直接执行 mitmproxy 即可,默认监听的是本地的8080 端口

1
mitmproxy

下载安装证书

  1. 下载证书
1
wget -e http_proxy=localhost:8080 http://mitm.it/cert/pem -O mitmproxy-ca-cert.pem
  1. 安装证书
1
2
3
4
# 1. 安装到系统的根证书中
mv mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy.crt
# 2. 更新系统根证书, 其实就是写入到了 /etc/ssl/certs/ca-certificates.crt 这里!
sudo update-ca-certificates

如何卸载证书,其实就是删除掉mitmproxy.crt, 然后sudo update-ca-certificates

抓取HTTPS流量

  1. 配置HTTP代理
1
2
3
4
5
6
7
8
9
10
export http_proxy=http://localhost:8080
export https_proxy=http://localhost:8080

# 关于 no_proxy 环境变量,这篇文章介绍的比较好 https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/
# export no_proxy=localhost

# 如何删除/取消环境变量?
# unset http_proxy
# unset https_proxy
# unset no_proxy
  1. curl发起请求测试
1
curl https://www.douyin.com/
  1. 查看数据包(这个页面是一个交互式的页面)

image-20240228124623612

  1. 查看数据包详情

image-20240228124559445

  1. 应用程序开启抓包,通常情况下大部分开发语言的框架/应用程序的都是有适配 http_proxy 的(不明确的可以自行百度),因此开启应用程序抓包只需要给程序配置 https_proxyhttp_proxy 相关环境变量然后重启下应用程序即可!

HTTPS抓包原理

1. https_proxy 的工作原理

HTTPS代理服务器的工作原理基于HTTP的CONNECT方法。与普通的HTTP代理(根据客户端的GET,POST等请求,代理服务器会直接进行处理并将结果返回给客户端)有所不同,HTTPS代理主要用于建立一个TCP的隧道,用于客户端和目标服务器的相互通信。

第一步:客户端向代理服务器发送一个CONNECT请求,请求中包含目标服务器的地址和端口号!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
~/.mitmproxy curl 'https://www.douyin.com' -v
* Uses proxy env variable https_proxy == 'http://localhost:8080'
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.douyin.com:443
> CONNECT www.douyin.com:443 HTTP/1.1
> Host: www.douyin.com:443
> User-Agent: curl/7.64.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.0 200 Connection Established
<
* Proxy replied 200 to CONNECT request

上面这个日志我们可以看到请求Host是www.douyin.com:443

第二步:如果代理服务器允许此连接,它会与目标服务器建立TCP连接,并向客户端发送一个状态行,比如 “HTTP/1.0 200 Connection Established”,告知客户端可以开始发送请求到目标服务器了!

第三步:代理服务器主要扮演数据转发的角色,客户端与目标服务器之间可以通过这个隧道来发送任何类型的数据,包括但不限于HTTP请求,也可以能是websocket等!

2. https_proxy 抓包原理

MITM

上面我们介绍了https proxy的原理,第三步的时候实际上我们做了一个tcp代理,此时就能下文章了!官方术语叫做MITM (Man-In-The-Middle)中间人攻击,其实就是伪造服务端的证书进行实现的,然后骗取客户端提前信任 MITM 服务的根证书!

image-20240301152804082

根证书

如何伪造服务端的证书?首先要熟悉TLS认证(握手 handshake)流程,其中一步是客户端获取证书,此时代理服务器(mitm)会伪造一份证书(公钥)发给客户端,客户端拿到证书后会进行证书的认证,这个是一个证书链认证的过程,其中最核心的就是根证书!如果客户端信任的了根证书,那么基于根证书分发的证书都会被认证!

TLS 握手

证书链,实际上我们通过浏览器就能获得,例如 https://excalidraw.com/ 这个网站的根证书是ISRG Root X1 签发的,然后我们打开 mac 的钥匙串,既可以看到确实已经信任了根证书ISRG Root X1

image-20240301153244627

image-20240301154138841

备注:如何生成一个根证书

1
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3650 -nodes

伪造证书

所以MITM的思路就是我自己签发一个根证书,然后让客户端所在的操作系统信任我的根证书,当客户端发起请求的时候,用我的根证书去给客户端访问的域名或者地址签发一个证书基于根证书的私钥,那么客户端就会认证成功!MITM也能成功劫持(Hijack)请求!

sequenceDiagram
客户端->>MITM: 1. 发起 CONNECT www.douyin.com:443 请求
MITM-->>客户端: 响应 HTTP/1.0 200 Connection Established
客户端 ->> MITM: 2. 发起 https 的握手请求
MITM ->> MITM: 3. 基于根证书为 www.douyin.com 生成一份证书
MITM -->> 客户端: 响应证书
客户端 ->> 客户端: 4. 认证证书成功, 握手成功
客户端 ->> MITM: 5. 发起HTTPS请求 https://www.douyin.com/
MITM ->> MITM: MITM这里是可以篡改、抓取请求/响应的
MITM ->> 服务端: 6. 发起请求
服务端 -->> MITM: 返回响应 
MITM -->> 客户端: 返回响应

其中第四步,由于已经提前信任了根证书,为此客户端一定会认证成功!关于伪造证书的生成逻辑下文代码会有!

3. 代码实现

  1. http代理逻辑
  2. 证书相关逻辑

其他

服务间调用的流量大部分都是RPC流量,Thrift/HTTP占据大部分,那么我能不能直接在机器上抓和解析Thrift包呢?是的可以,我本人开发了一个工具可以读取tcpdump的输出,来实时解析thrift/http报文,有兴趣的同学可以看一下 https://github.com/Anthony-Dong/golang/tree/master/command/tcpdump 这个,使用起来会非常的简单,目前我在公司内部也经常使用和处理线上问题!

本人坚持原创技术分享,如果你觉得文章对您有用,请随意打赏! 如果有需要咨询的请发送到我的邮箱!