mitmproxyでHTTPSプロキシする時、どんな通信が発生しているのか追ってみる

前回の記事で、ブラウザからアクセスするURLはそのままで接続先サーバだけ変える方法と、その際のHTTP通信の様子を確認しました。今回はその続きで、HTTPSの場合について、ブラウザとmitmproxy間、mitmproxyとサーバ間の通信内容を追っていきたいと思います。

処理の概要

HTTPSプロキシする場合のmitmproxyの挙動について書かれたドキュメントがあるので、そちらを参考にして概要を掴みます。以下はそのドキュメントから引用した図です。

f:id:ks888:20170504194435p:plain

この図に基づいて、順番に説明していきます。

1-2: ブラウザとプロキシ間でHTTP CONNECT

最初にHTTP CONNECTでブラウザとプロキシ間をつなぎます。通常のプロキシの場合はここでプロキシ先サーバとのTCP接続(HTTPトンネル)を確立するのですが、mitmproxyはしません。

mitmproxyはHTTPSリクエストの確認・変更に対応するため、ブラウザとプロキシ間、プロキシとサーバ間それぞれでTLS接続をします。そのため、ここでHTTPトンネルを作る必要はありません。

3,6: ブラウザとプロキシ間でTLS接続確立

次にブラウザとプロキシ間でTLSハンドシェイクをします。あとでWiresharkで確認しますが、3でClient Hello、6で残りのやりとりをしています。4-5ではプロキシとサーバ間でTLSハンドシェイクをしています。

なぜこのような順序なのかというと、mitmproxyが独自のサーバ証明書を作成する際、証明書に紐付けるドメイン名を明らかにするためです。この情報は、本物のサーバ証明書Common NameSubjectAltNameという箇所に書かれています。今回初めて知ったのですが、サーバ証明書には複数のドメイン名を紐付けられるらしいです。実際www.google.co.jpにアクセスしたときのサーバ証明書を見てみると、Common Name*.google.comで、SubjectAltNameは以下のようなドメイン名の列になっています。*.google.co.jpyoutube.comもここにあります。

f:id:ks888:20170504194946p:plain:w500

これらの情報を取得してからサーバ証明書を作るため、このような実行順序になっているようです。

4-5: プロキシとサーバ間でTLS接続確立

3,6で書いたような目的のために、ここでプロキシとサーバ間でTLS接続を確立します。図にはTLSハンドシェイクをどこまで進めるのか書かれていませんが、実際の挙動としては最後まで進めているようです。

7-8: HTTPSリクエス

ここまででブラウザとプロキシ間、プロキシとサーバ間それぞれでTLS接続が確立されたので、あとはいつもどおりリクエストを送るだけです。ただし、前回の記事に書いたような方法で接続先サーバを変える場合、プロキシとサーバ間ではもう一度TLS接続が必要になります。接続先変更後のサーバとはTLS接続していないためです。

実際の通信をみてみる

次にWiresharkで実際の通信をみてみます。

今回はブラウザからhttps://example.com/にアクセスすると、実際はhttps://www.google.co.jpにアクセスするようにしてみました。mitmproxyは以下のように実行しました。プロキシのポートは8080番です。その他の設定については前回の記事を参考にしてください。

mitmproxy --no-http2 -s "./mitmproxy_replace_host.py example.com www.google.co.jp"

ブラウザからhttps://example.com/にアクセスすると、TLSとHTTPのレベルでは以下のようなパケットが飛んでいました。

f:id:ks888:20170504200011p:plain

大体ドキュメント通りに並んでいます。8で再びTLS接続をしていますが、これはスクリプトがリクエストを書き換えているためです。4-5ではhttps://example.com/TLS接続したのに対し、8ではhttps://www.google.co.jpTLS接続しています。

まとめ

HTTPSの場合について、ブラウザとmitmproxy間、mitmproxyとサーバ間の通信内容を確認しました。mitmproxy組込みのCA証明書を使うために、通常のHTTPSプロキシとは異なる手順を踏んでいることがわかりました。

参考にしたページ: http://docs.mitmproxy.org/en/stable/howmitmproxy.html