最近想要看一下專案程式打出去的 HTTPS 連線,內容有沒有正確,
想起了之前瞄過的 mitmproxy,決定這次就來試試看了。
試了一下發現還蠻好用的~
1. 安裝 mitmproxy
在 Mac 上可以用 Homebrew 安裝:
brew install mitmproxy
在 Ubuntu Linux 上可以用 apt 安裝:
sudo apt install mitmproxy
sudo apt install mitmproxy
sudo apt install mitmproxy
裝好之後,應該會有三個程式可以用,分別是:
- mitmproxy
- mitmweb
- mitmdump
以介面來說,當然是 mitmweb 會最容易操作,
不過像我是透過 SSH 連線來操作的話,用 mitmproxy 是比較合適的選擇~
2. 使用 mitmproxy 觀察 HTTPS 連線
首先在終端機裡執行
mitmproxy
mitmproxy
,這就會將 mitmproxy 跑起來,
並預設監聽在 8080 port。
假設這時我想要用 curl 連線到 https://www.google.com,
可以加上 -x http://127.0.0.1:8080 的參數,讓連線透過這個 proxy 出去。
不過此時通常會失敗,因為 mitmproxy 的 CA 並不被 curl 或系統信任:
$ curl -L -v -x http://127.0.0.1:8080 https://www.google.com
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.google.com:443
> CONNECT www.google.com:443 HTTP/1.1
> Host: www.google.com:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
< HTTP/1.1 200 Connection established
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
$ curl -L -v -x http://127.0.0.1:8080 https://www.google.com
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.google.com:443
> CONNECT www.google.com:443 HTTP/1.1
> Host: www.google.com:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
$ curl -L -v -x http://127.0.0.1:8080 https://www.google.com
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.google.com:443
> CONNECT www.google.com:443 HTTP/1.1
> Host: www.google.com:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
在 curl 裡可以再加上 -k 參數來忽略所有的 HTTPS 驗證錯誤,
這時 HTTPS 連線就可以成功透過 mitmproxy 連出去了:
$ curl -k -L -v -x http://127.0.0.1:8080 https://www.google.com
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.google.com:443
> CONNECT www.google.com:443 HTTP/1.1
> Host: www.google.com:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
< HTTP/1.1 200 Connection established
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* subject: CN=www.google.com
* start date: Dec 17 20:54:42 2021 GMT
* expire date: Dec 19 20:54:42 2022 GMT
* issuer: CN=mitmproxy; O=mitmproxy
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fcc4280c600)
> User-Agent: curl/7.64.1
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< date: Sun, 19 Dec 2021 12:55:28 GMT
$ curl -k -L -v -x http://127.0.0.1:8080 https://www.google.com
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.google.com:443
> CONNECT www.google.com:443 HTTP/1.1
> Host: www.google.com:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=www.google.com
* start date: Dec 17 20:54:42 2021 GMT
* expire date: Dec 19 20:54:42 2022 GMT
* issuer: CN=mitmproxy; O=mitmproxy
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fcc4280c600)
> GET / HTTP/2
> Host: www.google.com
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< date: Sun, 19 Dec 2021 12:55:28 GMT
......
$ curl -k -L -v -x http://127.0.0.1:8080 https://www.google.com
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.google.com:443
> CONNECT www.google.com:443 HTTP/1.1
> Host: www.google.com:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=www.google.com
* start date: Dec 17 20:54:42 2021 GMT
* expire date: Dec 19 20:54:42 2022 GMT
* issuer: CN=mitmproxy; O=mitmproxy
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fcc4280c600)
> GET / HTTP/2
> Host: www.google.com
> User-Agent: curl/7.64.1
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< date: Sun, 19 Dec 2021 12:55:28 GMT
......
這時在執行 mitmproxy 命令的終端機上,可以看到這個 GET 連線:

按下 ? 可以顯示快捷鍵,不過我平常用的就只有 <Enter> 顯示資料、
和 <q> 退出目前顯示資料、以及用上下方向鍵來選擇連線:

在最外面的連線列表,對某個連線按下 <Enter> 鍵後,
就會出現如下畫面,可以看到請求 (Request)、回應 (Response)、以及細節的部分:

在細節的部分,可以看到伺服器傳回來的簽章 (certificate) 資訊:

如果是自己寫的 Go 程式的話,在 mitmproxy 的 CA 不被信任時,
使用
http.client
http.client
通過 mitmproxy 的連線都會失敗。
這時可以在
tls.Config
tls.Config
裡設定
InsecureSkipVerify
InsecureSkipVerify
,
來略過簽章的驗證錯誤,這樣連線通過 mitmproxy 時才會成功:
config := &tls.Config{
InsecureSkipVerify: true
}
config := &tls.Config{
InsecureSkipVerify: true
}
使用 mitmproxy 的話,就可以清楚看到 HTTPS 連線裡的內容,
真的是很方便的好工具喔~
(本頁面已被瀏覽過 1,162 次)