アクセスの逆探知


コンピュータネットワークが一般の人々に急速に浸透した結果として、 ネットワークの匿名性を利用した犯罪や無責任な投稿が社会問題と なっています。この問題に対する唯一の対策は、アクセス主を特定できる IDを逆探知で割り出すことで、アクセス主に責任を持たせることです。

この章ではCGIへのアクセスを逆探知してアクセス主を特定する 手順の概略を紹介して、私が作成したCGI専用逆探知ライブラリ 「CGITrace」が提供する関数を使っての逆探知の方法を紹介します。


逆探知のメカニズム


Httpdは次の手順でCGI所有者が逆探知を行えるようにするための 情報を入手してCGIに渡してくれます。また、管理者による設定によって httpd自体にすべての逆探知を行わせることもできます。

  1. まず、Httpdが外部からアクセスを受けた時、httpdはそのアクセスの 通信経路のhttpd側の端子(ソケット)をシステムコールgetsockname()で 調べて、その通信相手のホストのIPアドレスを入手します。その値を 157.82.51.229のような文字列として環境変数 REMOTE_ADDR に設定します。
  2. 特に指定がない限り、httpdは、システムコールgethostbyaddr()を用いて 先に得たIPアドレスからホストの名前を逆引きします。 その文字列をそのまま環境変数 REMOTE_HOST に設定します。
  3. httpdによっては、通信経路の相手側の端子(ソケット)をシステム コールgetpeername()で調べて、その通信相手が通信に用いているソケットの port番号を入手します。その値を環境変数 REMOTE_PORT に設定します。
  4. CGIがこの REMOTE_ADDR と REMOTE_PORT とあらかじめわかっている SERVER_PORT の3つの値を受け取って、通信相手のホストに待機している ident daemon とコンタクトをとれば、そのREMOTE_PORTを使用している user名を入手することができます。

    管理者が httpd.conf ファイル中で「IdentityCheck on」とすれば、 httpdはすべてのアクセスに対して(4)の作業をhttpd内部で行い、 その結果のuser名を環境変数 REMOTE_IDENT に設定します。

    この手順から逆探知が成功するためには次の3つの条件が必要で あることがわかります。

    (1)の条件を満たすコンピュータは現在はまだ多くはありませんが、今後、 これを備えることを義務とする法律が整備されると思われます。
    ident daemonの役割を担うソフトには pident, authd など幾つかあり、 無償で配布されています。このソフトを起動するにはroot権限が必要です。 Web server管理者に導入を依頼して下さい。
    Web server管理者は利用者に不正アクセスを させないためにもident daemonを早急に導入するべきです。

    (2)の条件は比較的新しいapacheならこの条件を満たしています。

    (3)の条件が厄介で、アクセス主のsecurity保護のため最近流行っている firewallなのですが、逆探知も遮断してしまいます。


    CGI専用アクセス逆探知ライブラリ CGITrace の紹介


    私が作成した CGITrace をC/C++言語で作成されるUNIX上のCGIに 組み込むことで、そのCGIへのアクセス主のユーザー名を逆探知することが できます。

    ただし、逆探知を行うなら プライバシを 十分に尊重して下さい。 また、逆探知を 行っていることをなるべくHomepage上に明記してください。
    GetUser(), GetFinger() の実行はネットワークに対して重い負担となり ますので、頻繁にこれらの関数を連発しないようにCGIを工夫して下さい。

    このライブラリを利用するには、CGIプログラムで cgitrace.h を includeした上で、cgitrace.cc と一緒にコンパイルしてください。 もしくはobject fileを生成しておいてからリンクしてください。
    ライブラリ自体はC++言語で作成されていますが、C言語のCGIでも利用 できます。


    CGITraceライブラリのダウンロード


    CGITraceライブラリのsource file (cgitrace.cc, cgitrace.h) と document (cgitrace.html) の3つのfileをLZH形式で圧縮したものを 用意しましたのでdownloadして下さい。

    cgitrace.h
    cgitrace.cc

    ライブラリ関数の説明


    このライブラリが提供する関数は以下の通りです。

    void terror( const char *str );
    上記の関数が失敗した場合に、その失敗の原因を標準出力に出力します。 引数には適当な文字列を与えます。
    戻り値はありません。
    void twarning( void );
    CGIが逆探知を行っていることを一般閲覧者に知らせたほうが良い場合 にはこの関数を適当な所で実行して下さい。この関数は単に puts("This CGI has traced your access.");を実行します。
    int GetHost( char *host );
    アクセス主がログインしているマシンのホスト名と接続に使用した port番号を調べます。引数にはホスト名を格納するのに十分なサイズの 文字列配列を与えます。そのサイズは定数 HOST_SIZE に定められています。 アクセスが firewall や proxy を経由している場合にはそのマシンの ホスト名が格納されます。
    戻り値は失敗で -1 成功で 0 です。失敗の場合には原因を表す文字列が 引数に格納され、また terror()で詳細を表示できます。
    この関数は得られた値を環境変数 REMOTE_HOST に設定します。
    この関数をプログラム中で複数回実行することを 禁じます。

    [使用例]

        char host[HOST_SIZE];
        if( GetHost( host ) == -1 ){
          terror("GetHost");
        }else{
          printf("Remote host = %s<BR>\n", host );
        }
      
    [出力例]
        Remote host = somemachine.somewhere.co.jp.(1024)<BR>
      

    ()の中の数字がアクセス主が接続に使用したport番号です。

    int GetUser( char *user );
    アクセス主のログイン名をident daemonに尋ねます。 引数にはログイン名を格納するのに十分なサイズの文字列配列を与えます。 そのサイズは定数 USER_SIZE に定められています。 アクセスが firewall や proxy を経由している場合には単に それらのシステムの起動者の名前が格納されます。
    戻り値は失敗で -1 成功で 0 です。失敗の場合には原因を表す文字列が 引数に格納され、また terror()で詳細を表示できます。
    一定時間内に調査が完了しない場合には調査を 自動的に断念します。
    この関数をプログラム中で複数回実行することを 禁じます。
    この関数は逆探知して得られた値を REMOTE_IDENT に設定します。 REMOTE_IDENT が既に定義されていれば逆探知を行いません。

    [使用例]

        char user[USER_SIZE];
        if( GetUser( user ) == -1 ){
          terror("GetUser");
        }else{
          printf("Uesr name = %s<BR>\n", user );
        }
      
    [出力例]
        User name = naoki<BR>
      

    int GetAddr( char *addr );
    アクセス主がログインしているマシンのIPアドレスと接続に使用したport 番号を調べます。引数にはIPアドレスを格納するのに十分なサイズの文字列 配列を与えます。そのサイズは定数 ADDR_SIZE に定められています。
    戻り値は失敗で -1 成功で 0 です。失敗の場合には原因を表す文字列が 引数に格納され、また terror()で詳細を表示できます。

    [使用例]

        char addr[ADDR_SIZE];
        if( GetAddr( addr ) == -1 ){
          terror("GetAddr");
        }else{
          printf("Remote address = %s<BR>\n", addr );
        }
      
    [出力例]
        Remote address = 123.45.67.8.(1024)<BR>
      

    ()の中の数字がアクセス主が接続に使用したport番号です。

    int GetReferer( char *referer );
    アクセス主がどこのページのリンクを辿って来たのかを調べます。 引数にはページのURLを格納するのに十分なサイズの文字列配列を与えます。 そのサイズは定数 REFERER_SIZE に定められています。
    戻り値は失敗で -1 成功で 0 です。失敗の場合には原因を表す文字列が 引数に格納され、また terror()で詳細を表示できます。

    [使用例]

        char referer[REFERER_SIZE];
        if( GetReferer( referer ) == -1 ){
          terror("GetReferer");
        }else{
          printf("Referer URL = %s<BR>\n", referer );
        }
      
    [出力例]
        Referer URL = http://www.mymachine.mydomain/index.html<BR>
      

    int GetAgent( char *agent );
    アクセス主が使用している browser の種類を調べます。引数には browserの 種類を表す文字列を格納するのに十分なサイズの文字列配列を与えます。 そのサイズは定数 AGENT_SIZE に定められています。
    戻り値は失敗で -1 成功で 0 です。失敗の場合には原因を表す文字列が 引数に格納され、また terror()で詳細を表示できます。

    [使用例]

        char agent[AGENT_SIZE];
        if( GetAgent( agent ) == -1 ){
          terror("GetAgent");
        }else{
          printf("Agent = %s<BR>\n", agent );
        }
      
    [出力例]
        Agent = Mozilla/3.01 (X11; I; FreeBSD 2.1.0-RELEASE i386)<BR>
      

    int GetHandle( char *handle );
    .htaccess等でページ作者が定めた認証用の個人名を調べます。引数には 個人名を表す文字列を格納するのに十分なサイズの文字列配列を与えます。 そのサイズは定数 HANDLE_SIZE に定められています。
    戻り値は失敗で -1 成功で 0 です。失敗の場合には原因を表す文字列が 引数に格納され、また terror()で詳細を表示できます。

    [使用例]

        char handle[HANDLE_SIZE];
        if( GetHandle( handle ) == -1 ){
          terror("GetHandle");
        }else{
          printf("Handle name = %s<BR>\n", handle );
        }
      
    [出力例]
        Handle name = naoki<BR>
      

    int GetFinger( char lines[][FINGER_SIZE] );
    アクセス主がログインしているマシンに対してfingerを試みます。引数には fingerの返答情報を格納するのに十分なサイズの2次元文字列配列を与えます。 その下位サイズは定数 FINGER_SIZE に定められています。上位サイズは定数 FINGER_LINES に定められています。
    戻り値は失敗で -1 成功で受け取った行数です。失敗の場合には、terror() で詳細を表示できます。
    一定時間内に調査が完了しない場合には調査を自動的に断念します。
    この関数をプログラム中で複数回実行することを禁じます。

    [使用例]

        int i, num;
        char finger[FINGER_LINES][FINGER_SIZE];
        if( (num=GetFinger( finger )) == -1 ){
          terror("GetFinger");
        }else{
          puts("<PRE>");
          for( i=0 ; i<num ; i++ ){
            fputs( finger[i], stdout );
          }
          puts("</PRE>");
        }
      
    [出力例]
        
        Login    Name                 TTY  Idle  Login Time   Where
        naoki    Naoki Watanabe        p0        Sat    19:29 :0.0
        

    int GetTime( char *time );
    現在時刻を表す文字列を設定します。引数には現在時刻を表す文字列を 格納するのに十分なサイズの文字列配列を与えます。そのサイズは定数 TIME_SIZE に定められています。
    戻り値は常に 0 です。

    [使用例]

        char time[TIME_SIZE];
        GetTime( time );
        printf("Current time = %s<BR>\n", time );
      
    [出力例]
        Current time = 26/Dec/1998:21:56:41<BR>
      


    工夫のためのヒント


    逆探知の作業はネットワークに少なからぬ負担を掛けますので 逆探知の実行が必要最小限になるようにCGIを工夫して作って下さい。

    Chatのように頻繁に投稿があるCGIでは、逆探知回数を減らすために ホスト、ハンドル名が同じなら逆探知を繰り返さないようにすると 良いでしょう。

    Browserの機能である cookie を利用すれば、一度逆探知に成功した人の アクセスでは、逆探知しなくても同一人物であることがわかるので これを利用すると良いでしょう。

    アクセス元にident daemonが走っていなくても finger daemonが走って いるなら GetFinger() 関数でアクセス主を特定できる可能性があります。

    Firewallやproxy経由のアクセスでも、深刻な不正アクセスの主を特定する 捜査が必要である場合には、そのプロバイダ管理者にそのアクセスの [GetHost()の値]、[時刻]、[URL]の情報を添えて捜査を依頼して下さい。 そのマシンの記録と照合して犯人を特定できるかもしれません。


    著作権 免責



    目次

    Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
    渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp