コマンドラインオプションを隠す。

こんにちは、らいあです。
Dinoのプログラマーは毎日いろんなツールや関数を作って遊んでいるばかりではなくて、うまく案件を運用するという責務も負っていたりします。

今日、とある開発サーバをぼんやりと覗いていたところ、bash_historyに次のような行が残っている事に気づきました。

mysql -u hogehoge –password=hogehoge hogehoge_db

そうです、履歴にパスワードが残っているんです。担当者がパスワードをコマンドライン引数に指定して作業をしていたようです。気軽に手打ちするためにしていたようですが、実際の本番運用でこのようなことがあってはなりません。

historyに残る以外に、コマンドライン引数は、ps等のproc系コマンドでも確認することができます。長期間動作するようなプロセスがあった場合には、引数に重要な情報を指定していると、psで読み取られてしまったりします。だからこそますます引数には気をつけないといけないわけです。

・・・と思ってためしにmysql –password=hogehoge と実行してpsで見てみたところ、下のような表示になりました。

mysql –password=x xxxx

見えてしまうかと思いきや、xxxxとマスクされています。
これはどうやって実現されているのでしょうか? ・・・今日の調べ物はこの疑問からスタートしました。

まず『psで”password”のような引数だったら隠しているのかな』と思って、procpsを拾ってきて調べてみたのですが、単に/procを読み取って表示しているだけでした。
http://procps.sourceforge.net/ procps

実際、/procのcmdlineをcatしてみると、すでにxxxxになっていました。
ではlinux(実験していたのはCentOSでした)のfs/procでなにかをしているのかな、と思ってbase.cを見てみたのですが、単にmm->arg_startからmm->arg_endまでを取得しているだけでした。
http://www.kernel.org/pub/linux/kernel/v2.6/ kernel/v2.6

ようやくここで『じゃあmysqlコマンドの段階でなにかをしているのかな』と気づきまして、mysqlクライアントを見てみますと、次のようなコードがありました。

      while (*argument) *argument++= 'x'; // Destroy argument

passwordの値が入っているargument変数をxxxxで上書きしています。このargument変数の実体はmain関数に渡されているargv変数です。argv変数を書き換えることでプロセスのコマンド引数を上塗りすることができるのでしょうか?

試してみました。

// コマンドライン引数の頭の一文字をxにしている
int main(int argc, char* argv[])
{
    int i;
    for (i=0; i<argc; i++) {
        char* v = argv[i];
        v[0] = 'x';
    }
    sleep(10);
    return 0;
}

ちゃんともくろみ通り動きました!!。Linuxの場合、argvはプロセスのコマンドライン引数のメモリをそのまま指しているので、その指す値を書き換えることで、コマンドライン引数(arg_start)を上塗りできるようです。procfsではarg_startをそのまま表示しているので、”cat /proc/PID/cmdline”などとすると、書き換えたあとの値が表示されるわけです。

※PHPの話※

PHPでコマンドラインのプログラムを書くこともみなさんよくありますよね。PHPのプログラムでコマンドライン引数をマスクするにはどうしたらいいでしょうか?

答えは簡単で、このように-Hオプションを与えます。すると、”php”という$0相当の値だけが残って、それ以降のオプションがすべて上書きされます。

php -H hogehoge.php

これはphp_cli.cの当該部分です。確かにargvを上塗りしています。

  if (hide_argv) {
   int i;
   for (i = 1; i < argc; i++) {
    memset(argv[i], 0, strlen(argv[i]));
   }
  }

しかし、すべてのオプションを消す動作になってしまうので、mysqlコマンドのような『passwordだけxxxでマスクする』という理想的な動作は実現できません。
どなたかこれを実現する方法をご存知ないでしょうか?

※最後に※

この『argvを上塗りする』というのは一般常識的な仕様なのでしょうか?
自分はまったく知りませんでした。はたしてCの仕様的に正しいんでしょうか。と思って仕様を見てみましたところ、次のような記述がありました。

argv配列が指す文字列は、プログラムによって変更可能でなければならない。

http://www.jisc.go.jp/app/pager?id=86854 JIS規格

本当にこれがポータブルなやりかたなのかイマイチわかりません。BSDにはsetproctitle()という関数がある(そしてargvの上書きよりもそちらを使えとmanにはあります)こともわかりましたが、Linuxには無いようです。上述のfs/proc/base.cには『setproctitle()が使用された場合~』というようなコメントが残っていたので混乱しました。Linuxには無いようです!
http://www.digipedia.pl/man/setproctitle.3.html man

ちなみにPerlだとこれだけでできます。やっぱりPerlはいいですね。

$0 = 'hello perl world';

コメント / トラックバック 1 件

#1 通りすがり 2008/08/18 20:11

Unix Programming FAQ
1.13 どうすれば(psで見えるような)プログラムの名前を変更できますか?
http://www.adl.nii.ac.jp/~moro/unix-programmer/faq-j_toc.html#TOC22

遅レス失礼。やっぱりPerlはいいよね(笑

コメントする