仙石浩明の日記

2006年4月7日

DB サーバのセキュリティ向上策 (5)

前々回(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
Filed under: stone 開発日記,システム構築・運用 — hiroaki_sengoku @ 09:35

No Comments »

No comments yet.

RSS feed for comments on this post.

Leave a comment