【tor】Wireshark で HTTP/2 のパケットキャプチャ

Nghttp2

OSX10.10.2に brewで Nghttp2をインストールする。

$ brew install nghttp2

適当に index.htmlを作って置く。

$ cat /var/www/index.html 
<html>
	<body>Hello, World</body>
</html> 

まずは TLS を無効にして Server を起動する。

$ nghttpd -v --no-tls -d "/var/www" 8080 
IPv4: listen 0.0.0.0:8080
IPv6: listen :::8080
[id=1] [  1.906] send SETTINGS frame 
          (niv=1)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[id=1] [  1.907] recv SETTINGS frame 
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[id=1] [  1.907] recv SETTINGS frame 
          ; ACK
          (niv=0)
[id=1] [  1.907] recv PRIORITY frame 
          (dep_stream_id=0, weight=201, exclusive=0)
[id=1] [  1.907] recv PRIORITY frame 
          (dep_stream_id=0, weight=101, exclusive=0)
[id=1] [  1.907] recv PRIORITY frame 
          (dep_stream_id=0, weight=1, exclusive=0)
[id=1] [  1.907] recv PRIORITY frame 
          (dep_stream_id=7, weight=1, exclusive=0)
[id=1] [  1.907] recv PRIORITY frame 
          (dep_stream_id=3, weight=1, exclusive=0)
[id=1] [  1.907] recv (stream_id=13) :method: GET
[id=1] [  1.907] recv (stream_id=13) :path: /index.html
[id=1] [  1.907] recv (stream_id=13) :scheme: http
[id=1] [  1.907] recv (stream_id=13) :authority: 127.0.0.1:8080
[id=1] [  1.907] recv (stream_id=13) accept: */*
[id=1] [  1.907] recv (stream_id=13) accept-encoding: gzip, deflate
[id=1] [  1.907] recv (stream_id=13) user-agent: nghttp2/1.0.2
[id=1] [  1.907] recv HEADERS frame 
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
[id=1] [  1.907] send SETTINGS frame 
          ; ACK
          (niv=0)
[id=1] [  1.907] send HEADERS frame 
          ; END_HEADERS
          (padlen=0)
          ; First response header
          :status: 200
          server: nghttpd nghttp2/1.0.2
          content-length: 44
          cache-control: max-age=3600
          date: Sun, 30 Aug 2015 13:00:40 GMT
          last-modified: Sun, 30 Aug 2015 07:25:07 GMT
[id=1] [  1.908] send DATA frame 
          ; END_STREAM
[id=1] [  1.908] stream_id=13 closed
[id=1] [  1.908] recv GOAWAY frame 
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])
[id=1] [  1.908] closed

nghttp2付属の Client からアクセスする。

$ nghttp -nv https://127.0.0.1:8080/index.html
[  0.001] Connected
[  0.002] recv SETTINGS frame 
          (niv=1)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[  0.002] send SETTINGS frame 
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.002] send SETTINGS frame 
          ; ACK
          (niv=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.002] send HEADERS frame 
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /index.html
          :scheme: http
          :authority: 127.0.0.1:8080
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.0.2
[  0.003] recv SETTINGS frame 
          ; ACK
          (niv=0)
[  0.003] recv (stream_id=13) :status: 200
[  0.003] recv (stream_id=13) server: nghttpd nghttp2/1.0.2
[  0.003] recv (stream_id=13) content-length: 44
[  0.003] recv (stream_id=13) cache-control: max-age=3600
[  0.003] recv (stream_id=13) date: Sun, 30 Aug 2015 13:00:40 GMT
[  0.003] recv (stream_id=13) last-modified: Sun, 30 Aug 2015 07:25:07 GMT
[  0.003] recv HEADERS frame 
          ; END_HEADERS
          (padlen=0)
          ; First response header
[  0.004] recv DATA frame 
          ; END_STREAM
[  0.004] send GOAWAY frame 
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

TLSを無効にしているため, Wiresharkでもそのまま平文の通信が確認できる。

暗号化(TLS)でHTTP/2

Wireshark (v1.12.7)で, デフォルトとも言える暗号化された HTTP/2通信の中を覗くことを目指す。

実験なので適当にオレオレ証明書を作る。

$ openssl genrsa 2048 > server.key 
$ openssl req -new -key server.key > server.csr
$ openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
$ rm server.csr

作成した鍵・証明書を指定して nghttpdを起動する。

$ nghttpd -v -d "/var/www" 8080 server.key server.crt

話が逸れるが, curlの Extension機能を使うと –http2 でHTTP/2 Clientになれる。

$ brew install curl --with-nghttp2 --with-openssl
$ curl --version
curl 7.43.0 (x86_64-apple-darwin14.1.0) libcurl/7.43.0 OpenSSL/1.0.2c zlib/1.2.5 nghttp2/1.0.2
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp 
Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets 

$ curl --http2 --insecure https://127.0.0.1:8080/index.html
<html>
	<body>Hello, World</body>
</html> 

また, Chrome / Firefox から https://127.0.0.1:8080/index.html にアクセスして, Hello, World が表示されることを確認。

WiresharkでHTTP/2のパケットキャプチャ

本題のパケットキャプチャ。OSXで CLIから Firefoxを起動するときに, 環境変数に SSLKEYLOGFILEの場所を設定する。[1]
これは, NSS Key Log Formatというらしい。
起動したら https://127.0.0.1:8080/index.html にアクセスする。

$ SSLKEYLOGFILE=~/Desktop/tls_key_firefox.log "/Applications/Firefox.app/Contents/MacOS/firefox-bin"

$ cat ~/Desktop/tls_key_firefox.log
# SSL/TLS secrets log file, generated by NSS
CLIENT_RANDOM d33200b945a2193b357bbb6aeb75857f0ca042ae2bd8f1abaca4d0ee84647f3c dbda6eddb91bf5678b17a9418e1609fcaa72ab60e33478904f16bfcac8656437b3170cb225b3e91f785b7a781f719169
...

作成された SSLKEYLOGFILE を Wireshark に設定する。
Edit > Preferences > Protocols > SSL > (Pre)-Master-Secret log filename に指定する。[2]

http2-tls-key-setting

Applyで適用すると, Protocol がHTTP2と表示される。画面下部のtabで Frameのままだと暗号化されたままなので, 横の Decrypted SSL Dataを選択すると復号された Hello, Worldが確認できた。

http2-decrypted-packets

Nginxは HTTP/2用の Patchが出ていて, 正式対応も近いらしいので楽しみです。

[追記] 匿名接続ツール Tor を使ってみた

匿名接続ツールの Tor (The Onion Router) を OSX で使ってみたのでメモ。

$ brew install tor
  /usr/local/Cellar/tor/0.2.9.8: 20 files, 9.3M

まずは, Tor を使わずに curl でとあるホストに接続し サーバログ (LTSV) を調べる。

$ curl -vvv -sL https://www.example.jp/ > normal.log
*   Trying 160.16.116.76...
* Connected to www.example.jp (160.16.116.76) port 80 (#0)
> GET / HTTP/1.1
> Host: www.example.jp
> User-Agent: curl/7.49.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.4.6 (Ubuntu)
< Date: Sun, 15 Jan 2017 08:02:07 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding, Cookie
< Cache-Control: no-cache
< WP-Super-Cache: Served supercache file from PHP
< Expires: Sun, 15 Jan 2017 08:02:06 GMT
< Cache-Control: private
<
{ [13096 bytes data]
* Connection #0 to host www.example.jp left intact

Torサーバ を経由していないので, IPアドレスは日本国内。

domain:www.example.jp	host:106.154.40.xxx	user:-	time:15/Jan/2017:16:59:00 +0900	method:GET	path:/	protocol:HTTP/1.1	status:200	size:32903	referer:-	agent:curl/7.49.1	response_time:0.226	cookie:-	set_cookie:-	upstream_addr:127.0.0.1:9000	upstream_cache_status:-	upstream_response_time:0.226

続いて `tor &` で Torサーバを立ち上げておいて, `–socks5`オプション で指定し curl を実行する。

$ curl -vvv -sL --socks5 127.0.0.1:9050 https://www.example.jp/ > tor.log
*   Trying 127.0.0.1...
* 160
* 16
* 116
* 76
* Connected to 127.0.0.1 (127.0.0.1) port 9050 (#0)
> GET / HTTP/1.1
> Host: www.example.jp
> User-Agent: curl/7.49.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.4.6 (Ubuntu)
< Date: Sun, 15 Jan 2017 08:03:13 GMT
< Content-Type: text/html; charset=UTF-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding, Cookie
< Cache-Control: no-cache
< WP-Super-Cache: Served supercache file from PHP
< Expires: Sun, 15 Jan 2017 08:03:12 GMT
< Cache-Control: private
<
{ [3102 bytes data]
* Connection #0 to host www.example.jp left intact

IPアドレスから地域を調べると ポーランド だったので Tor サーバをいくつか経由していそうだ。

domain:www.example.jp	host:178.217.187.39	user:-	time:15/Jan/2017:17:03:13 +0900	method:GET	path:/	protocol:HTTP/1.1	status:200	size:32903	referer:-	agent:curl/7.49.1	response_time:0.005	cookie:-	set_cookie:-	upstream_addr:127.0.0.1:9000	upstream_cache_status:-	upstream_response_time:0.005


[1] HTTP/2 over TLS の通信をダンプする方法
[2] Decrypting TLS Browser Traffic With Wireshark – The Easy Way!