SST連載・解説記事

  1. HOMEHOME
  2. SSTコラムSSTコラム
  3. SSTtechlog
  4. 04 Fiddlerの紹介

SSTtechlog

対象読者
Webアプリケーションに関わるエンジニア全般

はじめに

今回の執筆者プロフィール

長谷川 洋介(はせがわ ようすけ)

株式会社セキュアスカイ・テクノロジー
技術顧問 はせがわようすけ
Facebook

技術革新の速いWebセキュリティに関連する研究において長期にわたり最先端で活躍。国内、海外でのカンファレンスでの講演や記事執筆も多数。2015年7月より株式会社セキュアスカイ・テクノロジーの常勤技術顧問に就任。


セキュアスカイ・テクノロジーのはせがわようすけです。

先日Facebookに上げられたとあるワーキンググループ(後の懇親会)に関する投稿で、私を含む3人が同じポーズで写真を撮られていたことがわかりました。
偶然その3人が別々のウェブプロキシツールを使っていたため、並べるとプロキシツールのエバンジェリストっぽいと言われました。日々プロキシツールの普及に努めている本物のエバンジェリストさんに失礼なので、ジェリストくらいでいいです…。
とはいえ折角の機会なので、私が普段愛用しているプロキシツール「Fiddler」について紹介したいと思います。

techlog4コマ02

illustrations by あおい海月



Fiddlerはプロキシとして通信ログを確認する以外にも様々な機能を持っており、その中で私がよく使う機能は「AutoResponder」です。診断やデバッグに覚えておくと非常に便利な機能です。詳細は割愛します。

一方、最も好きな機能は「FiddlerScript」です。この機能は、スクリプト言語によってFiddlerのリクエストやレスポンスを操作したりUIを書き換えたりと、Fiddlerの多くの部分を自動化したりカスタマイズすることができます。


Fiddlerを自分でカスタマイズする

FiddlerScriptの実体はCustomRules.jsというファイルにJScript.NETで記述されたコードです。CustomRules.jsを編集するには、「Rules」メニューの「Customize Rules...」をクリックします。初めてこの項目をクリックした場合にはFiddlerScript EditorというFiddlerScript編集のための専用ツールをインストールするか否かを選択するダイアログが表示されます。

FiddlerScript編集ダイアログ


ここで「はい」をクリックすると、FiddlerScript Editorのインストーラがダウンロードされます。「いいえ」をクリックした場合にはメモ帳でFiddlerScriptが開かれます。
なお、一度でも「いいえ」をクリックしてしまうと、以降「Customize Rules...」をクリックしても常にメモ帳でFiddlerScriptが開かれますが、レジストリの HKCU\Software\Microsoft\Fiddler2\Prefs\.default の fiddler.inspectors.response.advertisefse を "True" に設定することで、再び FiddlerScript Editor のインストールを促すダイアログを表示させることが可能です。

また、事前に「Tools」「Fiddler Options...」メニューの「Tools」タブ内にある「FiddlerScript Editor」を設定しておくことで、普段使い慣れている任意のエディタを使ってFiddlerScriptを変更することもできます。

FiddlerScriptでは、CustomRules.js内に記述されているHandlersクラスの各メソッドを編集することでFiddlerの動作をカスタマイズすることができます。
例えば、Fiddlerでは画面左側のセッション一覧にはHTTPメソッドが表示されていないのですが、以下のようなコードを追加することでセッション一覧にHTTPメソッドが表示されるようになります。

class Handlers
  {
    public static BindUIColumn("Method", 60)
    function FillMethodColumn(oS: Session): String {
       return oS.RequestMethod;
    }
    // 以下略
  }
  

また以下のようなコードを追加することで、「Rules」メニューの下に「force https for SST」のメニュー項目が追加され、これをクリックして選択状態にすると「www.securesky-tech.com」へのアクセスにはFiddlerからWebサーバの間は常にhttpsを利用するように強制させることができます。

  class Handlers
  {
      // メニューに「force https for SST」のメニュー項目を追加する
      // 設定された値はレジストリ内に保存される
      public static RulesOption( "force https for SST" ) 
      BindPref( "fiddlerscript.rules.forceHttps" )      
      var m_forceHttps: boolean = false;
  
      // 略
  
      static function OnBeforeRequest(oSession: Session) {
          // 「force https」が選択されており、ホスト名が
          // www.securesky-tech.com のときは https で接続する
          if( m_forceHttps && 
            ( oSession.host === "www.securesky-tech.com" ) && 
            ( oSession.oRequest.headers.UriScheme=="http" ) )
          {
              oSession.oRequest.headers.UriScheme = "https";
          }
          // 以下略
      }
      // 以下略
  }
  

このように、Fiddlerの挙動を柔軟にカスタマイズする様々な例が公式サイトに掲載されていますので、必要に応じて手元のCustomRules.jsを変更するといいでしょう。

特定条件に合致するリクエストにのみ反応する設定方法

さらに、冒頭で触れたAutoResponder機能とFiddlerScript を組み合わせることで、特定条件に合致するリクエストの場合にのみFiddlerScriptによって動的に生成したレスポンスをブラウザへ応答するといったことも可能になります。

まずは①のAutoResponderタブを開き、②の「Add Rule」をクリックして新しいルールを作成します。
③のマッチ条件にはここでは例として正規表現regex:(?inx)^http://example\.jp/.*を指定します。
これは、http://example.jp/で始まるURLに合致します (FiddlerのAutoResponderでは正規表現は.NET Frameworkの機能がそのまま使われていますので、フラグなどの詳細についてはRegular Expression Options等を参照して下さい)。
④の追加するルールのアクションテキストに*script:funcのように指定することで、マッチ条件に合致するリクエストに対してはFiddlerScript内の関数funcが呼び出されます。
マッチ条件、アクションテキストの両方を入力したら、忘れないように⑤の「Save」をクリックします。

AutoResponder機能を有効にするために⑥の「Enabled Rules」にチェックを入れることを忘れないようにしてください。また、⑦の「Unmatched request passthrough」にチェックを入れていないとルールに合致しないリクエストに対してブラウザに404応答を返してしまいますので、必要に応じてチェックを入れておきましょう。

さて、AutoResponder側の設定が終わりましたので、ルールに合致した場合に呼び出される関数funcをFiddlerScript内に作成します。

    class Handlers
    {
        public static function func( oSession: Session ){
            var html: String = "<html><body>Hello, " +
            "<b>NAME</b>-san</body></html>";
            var query:String = "";
            var m = /\?(.*)$/.exec( oSession.PathAndQuery );
            if( m !== null ){
                query = m[ 1 ];
            }
    
            // エスケープしてHTML内に文字列を埋め込み
            html = html.replace( /NAME/, htmlEscape( query.toUpperCase() ) );
            // 新しいレスポンスを生成
            oSession.utilCreateResponseAndBypassServer();
            // レスポンスヘッダの設定
            oSession.oResponse.headers.Add( "Content-Type", "text/html;charset=utf-8" );
            // ブラウザにレスポンスを返す
            oSession.ResponseBody = System.Text.Encoding.UTF8.GetBytes( html );
        }
        private static function htmlEscape( s: String ){
            return s.replace( /&/g, "&amp;" )
               .replace( /</g, "&lt;" )
               .replace( />/g, "&gt;" )
               .replace( /"/g, "&quot;" )
               .replace( /'/g, "&#x27;" );
        }
    
        // 以下略
    }
    

AutoResponderのマッチ条件に合致した場合、関数funcはSessionを引数に呼び出されます。 関数func内ではクエリ文字列を大文字に変換し、HTML内に埋め込んでブラウザにレスポンスとして返しています。もちろん、HTMLに埋め込む際にはきちんとエスケープしてXSSが発生しないようにもしています。

ポイントとしては、SessionオブジェクトのutilCreateResponseAndBypassServerメソッドを呼び出すことで実際のWebサーバへの接続をバイパスしてブラウザに返す新しいレスポンスを生成できることと、レスポンスボディにはバイト配列を与えるという2点になります。

AutoResponderとFiddlerScriptを組み合わせたもう少し複雑な例を見てみましょう。先ほど同様にAutoResponderではマッチ条件にregex:(?inx)^http://example\.jp/.*を設定します。またアクションテキストには*script:webServerを指定します。
CustomRules.jsのHandlersクラス内に先ほど指定した関数 webServer を実装します。

class Handlers
  {
      public static function webServer( oSession: Session ){
          var documentRoot:String = "c:/tmp/htdocs";
          var path:String = oSession.PathAndQuery.replace( /\?.*/g, "" );
          var realPath:String = documentRoot + path;
          var contentTypes:Object = {
              "txt" : "text/plain",
              "pdf" : "application/pdf",
              "html" : "text/html",
              "htm" : "text/html",
              "js" : "application/javascript",
              "css" : "text/css"
          };
          oSession.utilCreateResponseAndBypassServer();
          if( path.indexOf( "../" ) >= 0 || path.indexOf( "..\\" ) >= 0 ){
              // パス名に「../」「..\」が含まれていれば400応答
              oSession.oResponse.headers.Add( "Content-Type", "text/html;charset=utf-8" );
              oSession.oResponse.headers.HTTPResponseCode = 400;
              oSession.oResponse.headers.HTTPResponseStatus = "400 Bad Request";
          }else if( System.IO.File.Exists( realPath ) ){
              // パス名に相当するファイルを読み込んで返す
              var contentType:String = "application/octet-stream";
              var ext:String = ( /\.(.+)$/.exec( path ) || [] )[ 1 ];
              if( ext && contentTypes[ ext ] ){
                  // ファイル名の拡張子が登録済みであればそれに相当するContent-Typeを返す
                  contentType = contentTypes[ ext ];
              }
              oSession.oResponse.headers.Add( "Content-Type", contentType );
              oSession.ResponseBody = System.IO.File.ReadAllBytes( realPath );
          }else{
              // ファイルが見つからない場合は404応答を返す
              oSession.oResponse.headers.Add( "Content-Type", "text/html;charset=utf-8" );
              oSession.oResponse.headers.HTTPResponseCode = 404;
              oSession.oResponse.headers.HTTPResponseStatus = "404 Not Found";
          }
  
      }
  
      // 以下略
  }
  

このコードでは、FiddlerScript内にwebServerという関数を定義しています。このwebServerという関数では、リクエストされたURLに応じてC:\tmp\htdocs以下に存在しているファイルをブラウザへ応答しており、つまり静的なコンテンツを配信する名前通りのWebサーバとして機能しています。

Webアプリケーションの診断などにおいては、様々な加工されたレスポンスに対するブラウザの挙動の確認を行いたいという場面もよくありますが、そういった場合に手元にWebサーバを用意していなくても、手軽にFiddlerだけで動的なレスポンスを生成して応答させるという方法を知っていると、作業の効率も上がるかも知れませんね。

まとめ

今回は、FiddlerのAutoResponderとFiddlerScriptを組み合わせるというマイナーな機能を紹介しました。ジェリストなので。Fiddlerの主要機能やその他の機能については、エバンジェリストさんによる書籍をご覧下さい。

なお、本記事は三井物産セキュアディレクションの国分さんの記事を参考にさせて頂きました。国分さんありがとうございます。

参考資料

  • O'Reilly Japan - 実践 Fiddler
  • http://www.oreilly.co.jp/books/9784873116167/

    (2015年12月11日更新)

    今までのコラム

    Webセキュリティをざっくり理解するための3つのMAP

    Page Top