2007 年 6 月
はじめに
ウェブ サービスとやり取りするアプリケーションの開発には、特有の問題が伴います。サーバーに送信されたメッセージや受信したレスポンスの内容が正確にわからないと、ストレスがたまることがあります。追跡が最も難しいバグの一部は、サーバーに送信していると考えているものと、実際にワイヤーを通過しているものとの間に不一致があることが原因です。
この記事では、ネットワーク上のデータをより可視化し、有用なものにするのに役立つツールをいくつか紹介します。一般に「パケット スニファ」と呼ばれるこれらのツールは、ネットワーク インターフェースを通過するすべてのネットワーク パケットをキャプチャします。これらのパケットの内容と、送信および受信された順序を調べることは、デバッグに役立つ手法です。
例: 公開フィードを取得する
チャリティー ライドに参加するサイクリング チームを編成しており、説明会、チームの募金活動、トレーニング ライドなどのイベント用のカレンダーを作成しました。このカレンダーを公開して、チームメンバーや他のライダーがカレンダーを閲覧してイベントに参加できるようにしました。また、今後の予定を記載したニュースレターを送信したいので、Google カレンダーのウェブサイトから情報をコピーするのではなく、Google カレンダー データ API を使用してこのカレンダーをクエリし、予定を取得します。
Google Calendar API のドキュメントには、RESTful Google Data API を使用してプログラムでカレンダーを操作する方法が記載されています。(編集者注: v3 以降、Google カレンダー API は Google データ形式を使用しなくなりました)。まず、カレンダーの設定ページの ボタンをクリックして、カレンダーの予定フィードの URL を取得します。
http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic
Google カレンダーのドキュメントを参考に、次のようにカレンダーの予定を取得して表示できます。ここで、PUBLIC_FEED_URL
は予定フィードの URL を保持しています。
CalendarService myService = new CalendarService("exampleCo-fiddlerExample-1"); final String PUBLIC_FEED_URL = "http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic"; URL feedUrl = new URL(PUBLIC_FEED_URL); CalendarEventFeed resultFeed = myService.getFeed(feedUrl, CalendarEventFeed.class); System.out.println("All events on your calendar:"); for (int i = 0; i < resultFeed.getEntries().size(); i++) { CalendarEventEntry entry = resultFeed.getEntries().get(i); System.out.println("\t" + entry.getTitle().getPlainText()); } System.out.println();
これにより、カレンダーの予定の基本的なリストが生成されます。
All events on your calendar: MS150 Training ride Meeting with Nicole MS150 Information session
上記のコード スニペットではカレンダーの予定のタイトルが表示されますが、サーバーから受け取った残りのデータはどうなるのでしょうか?Java クライアント ライブラリでは、フィードやエントリを XML として簡単に出力することはできません。また、出力できたとしても、XML だけでは不十分です。リクエストに付随する HTTP ヘッダーはどうなりますか?クエリはプロキシまたはリダイレクトされましたか?オペレーションが複雑になるほど、これらの質問はますます重要になります。特に、問題が発生してエラーが発生した場合は重要です。パケット スニファリング ソフトウェアは、ネットワーク トラフィックを明らかにして、これらの質問に回答できます。
tcpdump
tcpdump は Unix 系のプラットフォームで動作するコマンドライン ツールですが、WinDump という Windows ポートもあります。ほとんどのパケット スニファと同様に、tcpdump はネットワーク カードを無差別モードにします。これにはスーパーユーザー権限が必要です。tcpdump を使用するには、リッスンするネットワーク インターフェースを指定するだけです。ネットワーク トラフィックは stdout に送信されます。
sudo tcpdump -i eth0
このコマンドを実行すると、さまざまなネットワーク トラフィックが大量に表示されます。その中には、認識できないものもあります。出力をファイルに転送して後で grep することもできますが、非常に大きなファイルになる可能性があります。ほとんどのパケット キャプチャ ソフトウェアには、必要なものだけをキャプチャするためのフィルタリング メカニズムが組み込まれています。
tcpdump は、ネットワーク トラフィックのさまざまな特性に基づくフィルタリングをサポートしています。たとえば、次の式にサーバーのホスト名を挿入して、ポート 80(HTTP メッセージ)でサーバーとの間で送受信されるトラフィックのみをキャプチャするように tcpdump に指示できます。
dst or src host <hostname> and port 80
フィルタ式に一致するパケットごとに、tcpdump はタイムスタンプ、パケットの送信元と宛先、いくつかの TCP フラグを表示します。この情報は、パケットの送信と受信の順序を示すため、有用です。
パケットの内容を確認することも重要です。「-A」フラグは、各パケットを ASCII で出力し、HTTP ヘッダーとメッセージ本文を表示するように tcpdump に指示します。-s フラグは、表示するバイト数を指定するために使用されます(-s 0 はメッセージ本文を切り捨てないことを意味します)。
これらをまとめると、次のコマンドになります。
sudo tcpdump -A -s 0 -i eth0 dst or src host <hostname> and port 80
このコマンドを実行してから、上記の短い .Java の例を実行すると、このオペレーションに関わるすべてのネットワーク通信が表示されます。トラフィックの中に HTTP GET
リクエストが表示されます。
22:22:30.870771 IP dellalicious.mshome.net.4520 > po-in-f99.google.com.80: P 1:360(359) ack 1 win 65535 E.....@....\...eH..c...P.=.....zP......GET /calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic HTTP/1.1 User-Agent: exampleCo-fiddlerExample-1 GCalendar-Java/1.0.6 GData-Java/1.0.10(gzip) Accept-Encoding: gzip Cache-Control: no-cache Pragma: no-cache Host: www.google.com Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive
Google データフィードを含む 200 OK
レスポンス メッセージも表示されます。フィードが 4 つのパケットに分割されていることに注目してください。
22:22:31.148789 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 1:1431(1430) ack 360 win 6432 E...1 ..2.I.H..c...e.P.....z.=.:P..M...HTTP/1.1 200 OK Content-Type: application/atom+xml; charset=UTF-8 Cache-Control: max-age=0, must-revalidate, private Last-Modified: Mon, 11 Jun 2007 15:11:40 GMT Transfer-Encoding: chunked Date: Sun, 24 Jun 2007 02:22:10 GMT Server: GFE/1.3 13da <?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gCal='http://sc hemas.google.com/gCal/2005' xmlns:gd='http://schemas.google.com/g/2005'><id>http ://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.goo gle.com/public/basic</id><updated>2007-06-11T15:11:40.000Z</updated><category sc heme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2 005#event'></category><title type='text'>MS150 Training Schedule</title><subtitl e type='text'>This calendar is public</subtitle><link rel='http://schemas.google .com/g/2005#feed' type='application/atom+xml' href='http://www.google.com/calend ar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic'></ link><link rel='self' type='application/atom+xml' href='http://www.google.com/ca lendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic ?max-results=25'></link><author><name>Lane LiaBraaten</name><email>api.lliabraa@ gmail.com</email></author><generator version='1.0' uri='http://www.google.com/ca lendar'>Google Calendar</generator><openSearch:totalRe 22:22:31.151501 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 1431:2861(1430) ack 360 win 6432 E...1!..2.I.H..c...e.P.......=.:P.. 2...sults>3</openSearch:totalResults><openSe arch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch :itemsPerPage><gd:where valueString=''></gd:where><gCal:timezone value='America/ Los_Angeles'></gCal:timezone><entry><id>http://www.google.com/calendar/feeds/24v j3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/dgt40022cui2k3j 740hnj46744</id><published>2007-06-11T15:11:05.000Z</published><updated>2007-06- 11T15:11:05.000Z</updated><category scheme='http://schemas.google.com/g/2005#kin d' term='http://schemas.google.com/g/2005#event'></category><title type='text'>M S150 Training ride</title><summary type='html'>When: Sat Jun 9, 2007 7am to 10am &nbsp; PDT<br> <br>Event Status: confirmed</summary><conte nt type='text'>When: Sat Jun 9, 2007 7am to 10am&nbsp; PDT<br> <b r>Event Status: confirmed</content><link rel='alternate' type='text/html' href='http://www.google.com/calendar/event?eid=ZGd0NDAwMjJjdWkyazNqNzQwaG5qNDY3 NDQgMjR2ajNtNXBsMTI1YmgyaWpiYm5laDk1M3NAZw' title='alternate'></link><link rel=' self' type='application/atom+xml' href='http://www.google.com/calendar/feeds/24v j3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/dgt40022cui2k3j 740hnj46744'></link><author><name>MS150 Training Schedule</name></author><gCal:s endEventNotifications value='false'></gCal:sendEventNotifications></entry><entry ><id>http://www.google.com/cal 22:22:31.153097 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 2861:4291(1430) ack 360 win 6432 E...1#..2.I.H..c...e.P.......=.:P.. ....endar/feeds/24vj3m5pl125bh2ijbbneh953s%4 0group.calendar.google.com/public/basic/51d8kh4s3bplqnbf1lp6p0kjp8</id><publishe d>2007-06-11T15:08:23.000Z</published><updated>2007-06-11T15:10:39.000Z</updated ><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.g oogle.com/g/2005#event'></category><title type='text'>Meeting with Nicole</title ><summary type='html'>When: Mon Jun 4, 2007 10am to 11am&nbsp; PDT<br> <br>Where: Conference Room B <br>Event Status: confirmed</summ ary><content type='text'>When: Mon Jun 4, 2007 10am to 11am&nbsp; PDT<br& gt; <br>Where: Conference Room B <br>Event Status: confirmed <br>Event Description: Discuss building cycling team for MS150</content><l ink rel='alternate' type='text/html' href='http://www.google.com/calendar/event? eid=NTFkOGtoNHMzYnBscW5iZjFscDZwMGtqcDggMjR2ajNtNXBsMTI1YmgyaWpiYm5laDk1M3NAZw' title='alternate'></link><link rel='self' type='application/atom+xml' href='http ://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.goo gle.com/public/basic/51d8kh4s3bplqnbf1lp6p0kjp8'></link><author><name>MS150 Trai ning Schedule</name></author><gCal:sendEventNotifications value='false'></gCal:s endEventNotifications></entry><entry><id>http://www.google.com/calendar/feeds/24 vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/va41amq3r08dhh kpm3lc1abs2o</id><published>20 22:22:31.190244 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: P 4291:5346(1055) ack 360 win 6432 E..G1$..2.K.H..c...e.P.....<.=.:P.. ....07-06-11T15:10:08.000Z</published><updat ed>2007-06-11T15:10:08.000Z</updated><category scheme='http://schemas.google.com /g/2005#kind' term='http://schemas.google.com/g/2005#event'></category><title ty pe='text'>MS150 Information session</title><summary type='html'>When: Wed Jun 6, 2007 4pm to Wed Jun 6, 2007 5pm&nbsp; PDT<br> <br>Event Statu s: confirmed</summary><content type='text'>When: Wed Jun 6, 2007 4pm to Wed Jun 6, 2007 5pm&nbsp; PDT<br> <br>Event Status: confirmed< /content><link rel='alternate' type='text/html' href='http://www.google.com/cale ndar/event?eid=dmE0MWFtcTNyMDhkaGhrcG0zbGMxYWJzMm8gMjR2ajNtNXBsMTI1YmgyaWpiYm5la Dk1M3NAZw' title='alternate'></link><link rel='self' type='application/atom+xml' href='http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.c alendar.google.com/public/basic/va41amq3r08dhhkpm3lc1abs2o'></link><author><name >MS150 Training Schedule</name></author><gCal:sendEventNotifications value='fals e'></gCal:sendEventNotifications></entry></feed>
この出力には、すべての HTTP ヘッダーとコンテンツ、およびいくつかの難解な TCP フラグが含まれています。すべてのデータがここに表示されますが、読み取りと理解が難しい場合があります。このデータを簡単に表示できるグラフィカル ツールがいくつかあります。
WireShark(旧 Ethereal)

WireShark は、ネットワーク トラフィックをさまざまな方法で表示します。
WireShark は、tcpdump と同じライブラリである libpcap で構築されたグラフィカル ツールで、Linux、Mac OS X、Windows で利用できます。WireShark の GUI を使用すると、パケット キャプチャ データを解釈して操作する新しい方法がいくつか可能になります。たとえば、ネットワーク インターフェースからパケットがキャプチャされると、使用されているプロトコルに基づいて異なる色で表示されます。トラフィックをタイムスタンプ、送信元、宛先、プロトコルで並べ替えることもできます。
パケットのリストで行を選択すると、Wireshark はパケット ヘッダーの IP、TCP、その他のプロトコル固有の情報を人間が読めるツリーで表示します。画面の下部には、データが 16 進数と ASCII で表示されます。
WireShark は視覚的にネットワーク トラフィックを把握しやすいですが、ほとんどの場合、ネットワーク トラフィックをフィルタリングする必要があります。WireShark には、数百ものプロトコルのサポートなど、強力なフィルタリング機能が備わっています。
ヒント: 使用可能なプロトコルを表示して複雑なフィルタを作成するには、WireShark ウィンドウの上部にある ボタンをクリックします。
上記の tcpdump の例で使用したフィルタを再作成するには、次の式を WireShark のフィルタ ボックスに挿入します。
ip.addr==<your IP address> && tcp.port==80
または、WireShark の HTTP に関する知識を活用します。
ip.addr==<your IP address> && http
これにより、キャプチャの結果が Google カレンダー サーバーとのこのやり取りに関与したパケットのみにフィルタされます。各パケットをクリックして内容を確認し、トランザクションを組み立てることができます。
ヒント: パケットの 1 つを右クリックして [Follow TCP Stream] を選択すると、リクエストとレスポンスが 1 つのウィンドウに順番に表示されます。
WireShark には、キャプチャ情報を保存する方法がいくつか用意されています。パケットは 1 つ、一部、またはすべてを保存できます。TCP ストリームを表示している場合は、[名前を付けて保存] ボタンをクリックするだけで、関連するパケットのみを保存できます。tcpdump キャプチャの出力をインポートして、WireShark で表示することもできます。
問題: SSL と暗号化
パケット キャプチャ ツールの一般的な欠点は、SSL 接続で暗号化されたデータを表示できないことです。上記の例では、一般公開フィードにアクセスするため、SSL は必要ありません。ただし、この例でプライベート フィードにアクセスする場合は、クライアントは Google 認証サービスで認証する必要があります。この認証には SSL 接続が必要です。
次のスニペットは前の例と似ていますが、ここでは CalendarService
がユーザーのカレンダー メタフィードをリクエストしています。これは認証が必要なプライベート フィードです。認証するには、setUserCredentials
メソッドを呼び出すだけです。このメソッドは、ClientLogin サービスへの HTTPS リクエストをトリガーし、レスポンスから認証トークンを取得します。以降のリクエストでは、CalendarService
オブジェクトに認証トークンが含まれるようになります。
CalendarService myService = new CalendarService("exampleCo-fiddlerSslExample-1"); myService.setUserCredentials(username, userPassword); final String METAFEED_URL = "http://www.google.com/calendar/feeds/default"; URL feedUrl = new URL(METAFEED_URL); CalendarFeed resultFeed = myService.getFeed(feedUrl, CalendarFeed.class); System.out.println("Your calendars:"); for (int i = 0; i < resultFeed.getEntries().size(); i++) { CalendarEntry entry = resultFeed.getEntries().get(i); System.out.println("\t" + entry.getTitle().getPlainText()); } System.out.println();
非公開の Google Data API フィードの認証とアクセスに必要なネットワーク トラフィックについて考えてみましょう。
- ClientLogin サービスにユーザー認証情報を送信します。
- メッセージ本文に次のパラメータを指定して、https://www.google.com/accounts/ClientLogin に HTTP
POST
を送信します。- メール - ユーザーのメールアドレス。
- Passwd - ユーザーのパスワード。
- source - クライアント アプリケーションを識別します。companyName-applicationName-versionID の形式にする必要があります。この例では、ExampleCo-FiddlerSSLExample-1 という名前を使用します。
- service - Google カレンダーのサービス名は「cl」です。
- メッセージ本文に次のパラメータを指定して、https://www.google.com/accounts/ClientLogin に HTTP
- 認証トークンを受け取る
- 認証リクエストが失敗すると、HTTP 403 Forbidden ステータス コードが返されます。
- 成功すると、サービスからのレスポンスは HTTP 200 OK ステータス コードと、レスポンスの本文にある 3 つの長い英数字コード(
SID
、LSID
、Auth
)になります。Auth
値は認証トークンです。
- 非公開カレンダーのメタフィードをリクエスト
- 次のヘッダーを使用して、http://www.google.com/calendar/feeds/default に HTTP
GET
を送信します。
Authorization: GoogleLogin auth=<yourAuthToken>
- 次のヘッダーを使用して、http://www.google.com/calendar/feeds/default に HTTP
このスニペットを実行し、WireShark でネットワーク トラフィックを表示してみてください(フィルタとして「http || ssl」を使用)。トランザクションに関与する SSL パケットと TLS パケットが表示されますが、ClientLogin リクエスト パケットとレスポンス パケットは「アプリケーション データ」パケットで暗号化されます。ご安心ください。この暗号化された情報を実際に明らかにするツールについては、次のセクションで説明します。
Fiddler
Fiddler は別のグラフィカル パケット スニッフィング ツールですが、これまで紹介したツールとは動作が大きく異なります。Fiddler は、アプリケーションとやり取りしているリモート サービス間のプロキシとして機能し、事実上中間者になります。Fiddler は、アプリケーションとリモート ウェブ サービスの両方との SSL 接続を確立し、一方のエンドポイントからのトラフィックを復号して、プレーンテキストをキャプチャし、トラフィックを再暗号化してから送信します。残念ながら、Fiddler は Windows でのみ利用可能です。Mac と Linux のユーザーの皆様には申し訳ありません。
注: SSL をサポートするには、Fiddler バージョン 2 と .NET Framework バージョン 2.0 が必要です。
Fiddler でネットワーク トラフィックを表示するには、主に [Session Inspector] タブを使用します。Google Data APIs の問題のデバッグに最も役立つサブタブは次のとおりです。
- Headers - HTTP ヘッダーを折りたたみ可能なツリー形式で表示します。
- Auth - 認証ヘッダーを表示します。
- Raw - ネットワーク パケットの内容を ASCII テキストで表示します。
ヒント: Fiddler ウィンドウの左下にある アイコンをクリックすると、キャプチャのオンとオフを切り替えることができます。
Fiddler は .NET Framework を使用して、Fiddler をプロキシとして使用するようにネットワーク接続を構成します。つまり、Internet Explorer または .NET コードを使用して行う接続は、デフォルトで Fiddler に表示されます。ただし、Java では HTTP プロキシの設定方法が異なるため、上記の Java サンプルのトラフィックは表示されません。
Java では、システム プロパティを使用して HTTP プロキシを設定できます。Fiddler はポート 8888 で実行されるため、ローカル インストールの場合、次の行を追加することで、Java コードで Fiddler を HTTP と HTTPS のプロキシとして使用できます。
System.setProperty("http.proxyHost", "localhost"); System.setProperty("http.proxyPort", "8888"); System.setProperty("https.proxyHost", "localhost"); System.setProperty("https.proxyPort", "8888");
これらの行を含むサンプルを実行すると、Java セキュリティ パッケージから不快なスタック トレースが返されます。
[java] Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Fiddler は SSL トラフィックを復号して表示できます。
このエラーは、SSL 接続でサーバーから返された証明書を検証できない場合に発生します。この場合、不正な証明書は中間者として動作する Fiddler から送信されています。Fiddler は証明書をオンザフライで生成します。Fiddler は信頼できる発行者ではないため、これらの証明書により Java で SSL 接続の設定に失敗します。
注: Fiddler の実行中に Internet Explorer で SSL 接続を行うと、信頼できない証明書を使用しても続行するかどうかを確認する「セキュリティ アラート」が表示されます。[View Certificate] をクリックすると、Fiddler が生成した証明書が表示されます。
では、このセキュリティ例外を回避するにはどうすればよいでしょうか。基本的には、すべての証明書を信頼するように Java のセキュリティ フレームワークを再構成する必要があります。幸いなことに、ここで車輪を再発明する必要はありません。Francis Labrie のソリューションを確認し、上記の例に SSLUtilities.trustAllHttpsCertificates()
メソッドを追加してください。
Java で Fiddler をプロキシとして使用するように構成し、デフォルトの証明書検証を無効にすると、例を実行して、ネットワーク経由で送信されるすべてのトラフィックを平文で確認できます。パスワードを盗まないでください。
この認証トランザクションは、SSL トラフィックのほんの一例にすぎません。一部のウェブ アプリケーションでは SSL 接続のみが使用されるため、データを復号する方法がないと HTTP トラフィックのデバッグはできません。
まとめ
tcpdump は Linux、Mac OS X、Windows で利用できます。探しているものがわかっていて、すぐにキャプチャする必要がある場合に最適なツールです。ただし、ネットワーク トラフィックを理解しやすい形式で表示するグラフィカル ツールもあります。tcpdump には、ここで説明したオプションやフィルタリング機能よりも多くのオプションとフィルタリング機能があります。tcpdump の機能の詳細については、「man tcpdump」と入力するか、オンラインの tcpdump のマニュアル ページをご覧ください。
WireShark は、Linux、Mac OS X、Windows でも利用できます。数百ものプロトコルを組み込みでサポートしているため、WireShark は HTTP デバッグだけでなく、多くのアプリケーションで役立つツールです。この概要では、WireShark の多くの機能のほんの一部しか紹介していません。詳しくは、「man wireshark」と入力するか、WireShark のウェブサイトをご覧ください。
Fiddler にも優れた機能が多数ありますが、SSL トラフィックを復号できる点が大きな特徴です。詳しくは、Fiddler2 のウェブサイトをご覧ください。
これらのパケット スニファ アプリケーションは、ツールボックスに入れておくと便利なツールです。また、これらはすべて無料です。Google API を使用しているときに不審な点が見つかった場合は、これらのネットワーク アナライザのいずれかを使用して、ネットワーク上のデータを詳しく調べてください。問題が見つからない場合は、いつでもディスカッション グループに質問を投稿できます。関連するネットワーク メッセージを含めることで、他のユーザーが特定の問題を理解して診断するのに役立ちます。
幸運を祈ります。