前々回(3)、 前回(4) と、 他人の DB パスワードを知ったユーザが、 その人になりすまして DB にログインしようとしても DBサーバへの通信を遮断する仕掛けについて説明しました。 (3) はクライアント側で、(4) はサーバ側で、 それぞれ DBサーバへの通信を監視し、 UNIX ユーザ名と、DB ユーザ名が一致しない場合は通信を遮断します。
DB サーバへの通信から DB ユーザ名を抽出する方法は、 DB サーバの種類によって異なるので、 専用のものを実装することになるのですが、 今回はその一例として MySQL 4.0.x の場合について説明しましょう。
MySQL の場合は、ソースが公開されていますし、 開発者向けにはプロトコル仕様書もあるようなので、 クライアント-サーバ間通信を完全に解析することも可能なのですが、 DBユーザ名を取り出す程度なら通信内容をダンプするだけでも 取り出し方が推測できます。
まずは手始めに通信内容をダンプしてみましょう。 どんなツールを使ってもよいのですが、 ここでは拙作 stone を使ってみます。 適当なポートで listen し、 それを MySQL サーバへ中継しつつ、 通信内容をダンプするように stone を実行します。 例えば、12345番ポートで listen し、 「-ppp」オプションを指定することによって通信内容をダンプさせ、 ホスト「dbserver」の 3306番ポートへ中継させるためには、 次のように stone を実行します。
stone -ppp dbserver:3306 12345
そして、MySQL のクライアントである mysql コマンドを実行します。 MySQL サーバに接続する代わりに、 stone が listen する 12345番ポートに接続させましょう。
mysql -P12345 -h127.0.0.1 -usengoku -pabcdefg
DB ユーザ名「sengoku」、パスワード「abcdefg」で接続します。 この時、stone の出力は次のようになります:
% stone -ppp dbserver:3306 12345 Apr 7 07:04:45.525293 16384 start (2.3a) [19635] Apr 7 07:04:45.530733 16384 stone 3: senri.gcd.org:3306 <- 0.0.0.0:12345 Apr 7 07:05:03.405877 16384 stone 3: accepted TCP 5 from 127.0.0.1:37758 mode=3 Apr 7 07:05:03.417693 16384 3 5<6 34 00 00 00 0a 34 2e 30 4....4.0 Apr 7 07:05:03.417785 16384 3 5<6 2e 32 35 2d 73 74 61 6e .xx-stan Apr 7 07:05:03.417802 16384 3 5<6 64 61 72 64 2d 6c 6f 67 dard-log Apr 7 07:05:03.417813 16384 3 5<6 00 e8 7e 00 00 2d 3a 79 ..~..-:y Apr 7 07:05:03.417823 16384 3 5<6 75 5e 58 7e 31 00 2c 20 u^X~1., Apr 7 07:05:03.417833 16384 3 5<6 0c 02 00 00 00 00 00 00 ........ Apr 7 07:05:03.417844 16384 3 5<6 00 00 00 00 00 00 00 00 ........ Apr 7 07:05:03.418337 16384 3 5>6 15 00 00 01 85 24 00 00 .....$.. Apr 7 07:05:03.418374 16384 3 5>6 00 73 65 6e 67 6f 6b 75 .sengoku Apr 7 07:05:03.418385 16384 3 5>6 00 53 4d 5c 4a 55 53 49 .SM\JUSI Apr 7 07:05:03.418393 16384 3 5>6 57 W Apr 7 07:05:03.418643 16384 3 5<6 05 00 00 02 00 00 00 02 ........ Apr 7 07:05:03.418671 16384 3 5<6 00 .
各行始めの「Apr 7 07:04:45.525293」は時刻で、マイクロ秒単位で表示しています (マイクロ秒単位まで表示するようになったのは stone 2.3a からです)。 次の「16384」はスレッドID ですが、今回は無視してもらって構いません。 最初の三行は stone のログなので無視するとして、 次の部分が MySQL サーバからクライアントへ送られた通信内容です。 「5<6」が、サーバ→クライアント方向の通信であることを示します。
Apr 7 07:05:03.417693 16384 3 5<6 34 00 00 00 0a 34 2e 30 4....4.0 Apr 7 07:05:03.417785 16384 3 5<6 2e 32 35 2d 73 74 61 6e .xx-stan Apr 7 07:05:03.417802 16384 3 5<6 64 61 72 64 2d 6c 6f 67 dard-log Apr 7 07:05:03.417813 16384 3 5<6 00 e8 7e 00 00 2d 3a 79 ..~..-:y Apr 7 07:05:03.417823 16384 3 5<6 75 5e 58 7e 31 00 2c 20 u^X~1., Apr 7 07:05:03.417833 16384 3 5<6 0c 02 00 00 00 00 00 00 ........ Apr 7 07:05:03.417844 16384 3 5<6 00 00 00 00 00 00 00 00 ........
MySQL のバージョン番号らしきものが送られている様子が分かります。 ここでの目的は DB ユーザ名を抽出することであり、 DB ユーザ名は、クライアント→サーバ方向に送られるはずなので、 サーバ→クライアント方向の通信は無視してしまっても構わないのですが、 先頭の「34 00」には注目しておいた方がよいでしょう。 この手のクライアント-サーバ間プロトコルでは、 先頭にこれから送るデータの長さを送信することが一般的だからです。 実際、「34 00」は十進数で言うと 52 ですが、 クライアントへ送られたデータは 56 バイトなので、 先頭 2 バイトがデータ長、次の 2 バイト「00 00」がデータの種別、 残りの 52 バイトがデータだろう、と推測できます。
続く部分が、クライアントからサーバへ送られた通信内容です。 「5>6」が、クライアント→サーバ方向の通信であることを示します。
Apr 7 07:05:03.418337 16384 3 5>6 15 00 00 01 85 24 00 00 .....$.. Apr 7 07:05:03.418374 16384 3 5>6 00 73 65 6e 67 6f 6b 75 .sengoku Apr 7 07:05:03.418385 16384 3 5>6 00 53 4d 5c 4a 55 53 49 .SM\JUSI Apr 7 07:05:03.418393 16384 3 5>6 57 W
先頭 2 バイト「15 00」がデータ長であると仮定してみます。 十進数で言うと 21 で、 データ種別と推測される 2 バイトデータ「00 01」の後に 21 バイトのデータが続いています。 そして、データの中に明らかに DB ユーザ名と思われる文字列「sengoku」と そのデリミタと思われる「00」があります。 続く 8 バイトはパスワードと推測できます。 DB ユーザ名の前の 5 バイトのデータ「85 24 00 00 00」は、 固定長のデータと推測できるので、 DBユーザ名を抽出するには、 クライアント→サーバ方向のデータのうち、 最初から 10 バイト目から「00」までを取り出せば良さそうです。
もちろん実際に運用する際は、MySQL のソースないしプロトコル仕様書を 参照して確実を期するべきですが、 プロトコル仕様が公式には公開されていない DB サーバの場合でも、
- DB ユーザ名が取り出せなかった場合は、ログに出力した上で通信を容認
- DB ユーザ名が取り出せたが、その DB ユーザ名が DB に存在しない場合は、 ログに出力した上で通信を容認
- DB ユーザ名が取り出せて、かつその DB ユーザ名が存在する場合は、 取り出した DB ユーザ名が正しいものとして扱い、通信の許可/遮断を行なう
などと、DB ユーザ名を正しく取り出せなかったと思われるときは とりあえず通信を容認しておいて、 後ほど DB ユーザ名の抽出方法を改訂する、という運用が可能でしょう。
なお、MySQL の場合は前述したように公式なプロトコル仕様書もありますし、 Ian Redfern 氏がソースを解析して作成したプロトコルの解説が 以下のページで公開されています。
- MySQL Protocol (MySQL 3.22 ~ 4.0)
- http://www.redferni.uklinux.net/mysql/MySQL-323.html
- MySQL Protocol (MySQL 4.1)
- http://www.redferni.uklinux.net/mysql/MySQL-Protocol.html